14fccf43aSdan
29b1c62d4Sdrh #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_SESSION) \
39b1c62d4Sdrh && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
44fccf43aSdan
54fccf43aSdan #include "sqlite3session.h"
64fccf43aSdan #include <assert.h>
74fccf43aSdan #include <string.h>
852b1dbb5Smistachkin #if defined(INCLUDE_SQLITE_TCL_H)
952b1dbb5Smistachkin # include "sqlite_tcl.h"
1052b1dbb5Smistachkin #else
1152b1dbb5Smistachkin # include "tcl.h"
12c0855684Smistachkin # ifndef SQLITE_TCLAPI
13c0855684Smistachkin # define SQLITE_TCLAPI
14c0855684Smistachkin # endif
1552b1dbb5Smistachkin #endif
164fccf43aSdan
17f1b40e83Sdan #ifndef SQLITE_AMALGAMATION
18f1b40e83Sdan typedef unsigned char u8;
19f1b40e83Sdan #endif
20f1b40e83Sdan
217531a5a3Sdan typedef struct TestSession TestSession;
227531a5a3Sdan struct TestSession {
237531a5a3Sdan sqlite3_session *pSession;
247531a5a3Sdan Tcl_Interp *interp;
257531a5a3Sdan Tcl_Obj *pFilterScript;
267531a5a3Sdan };
277531a5a3Sdan
284757c658Sdan typedef struct TestStreamInput TestStreamInput;
294757c658Sdan struct TestStreamInput {
304757c658Sdan int nStream; /* Maximum chunk size */
314757c658Sdan unsigned char *aData; /* Pointer to buffer containing data */
324757c658Sdan int nData; /* Size of buffer aData in bytes */
334757c658Sdan int iData; /* Bytes of data already read by sessions */
344757c658Sdan };
354757c658Sdan
36a87070a2Sdan /*
37a87070a2Sdan ** Extract an sqlite3* db handle from the object passed as the second
38a87070a2Sdan ** argument. If successful, set *pDb to point to the db handle and return
39a87070a2Sdan ** TCL_OK. Otherwise, return TCL_ERROR.
40a87070a2Sdan */
dbHandleFromObj(Tcl_Interp * interp,Tcl_Obj * pObj,sqlite3 ** pDb)41a87070a2Sdan static int dbHandleFromObj(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){
42a87070a2Sdan Tcl_CmdInfo info;
43a87070a2Sdan if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){
44a87070a2Sdan Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(pObj), 0);
45a87070a2Sdan return TCL_ERROR;
46a87070a2Sdan }
47a87070a2Sdan
48a87070a2Sdan *pDb = *(sqlite3 **)info.objClientData;
49a87070a2Sdan return TCL_OK;
50a87070a2Sdan }
51a87070a2Sdan
52a87070a2Sdan /*************************************************************************
53a87070a2Sdan ** The following code is copied byte-for-byte from the sessions module
54a87070a2Sdan ** documentation. It is used by some of the sessions modules tests to
55a87070a2Sdan ** ensure that the example in the documentation does actually work.
56a87070a2Sdan */
57a87070a2Sdan /*
58a87070a2Sdan ** Argument zSql points to a buffer containing an SQL script to execute
59a87070a2Sdan ** against the database handle passed as the first argument. As well as
60a87070a2Sdan ** executing the SQL script, this function collects a changeset recording
61a87070a2Sdan ** all changes made to the "main" database file. Assuming no error occurs,
62a87070a2Sdan ** output variables (*ppChangeset) and (*pnChangeset) are set to point
63a87070a2Sdan ** to a buffer containing the changeset and the size of the changeset in
64a87070a2Sdan ** bytes before returning SQLITE_OK. In this case it is the responsibility
65a87070a2Sdan ** of the caller to eventually free the changeset blob by passing it to
66a87070a2Sdan ** the sqlite3_free function.
67a87070a2Sdan **
68a87070a2Sdan ** Or, if an error does occur, return an SQLite error code. The final
69a87070a2Sdan ** value of (*pChangeset) and (*pnChangeset) are undefined in this case.
70a87070a2Sdan */
sql_exec_changeset(sqlite3 * db,const char * zSql,int * pnChangeset,void ** ppChangeset)71a87070a2Sdan int sql_exec_changeset(
72a87070a2Sdan sqlite3 *db, /* Database handle */
73a87070a2Sdan const char *zSql, /* SQL script to execute */
74a87070a2Sdan int *pnChangeset, /* OUT: Size of changeset blob in bytes */
75a87070a2Sdan void **ppChangeset /* OUT: Pointer to changeset blob */
76a87070a2Sdan ){
77a87070a2Sdan sqlite3_session *pSession = 0;
78a87070a2Sdan int rc;
79a87070a2Sdan
80a87070a2Sdan /* Create a new session object */
81a87070a2Sdan rc = sqlite3session_create(db, "main", &pSession);
82a87070a2Sdan
83a87070a2Sdan /* Configure the session object to record changes to all tables */
84a87070a2Sdan if( rc==SQLITE_OK ) rc = sqlite3session_attach(pSession, NULL);
85a87070a2Sdan
86a87070a2Sdan /* Execute the SQL script */
87a87070a2Sdan if( rc==SQLITE_OK ) rc = sqlite3_exec(db, zSql, 0, 0, 0);
88a87070a2Sdan
89a87070a2Sdan /* Collect the changeset */
90a87070a2Sdan if( rc==SQLITE_OK ){
91a87070a2Sdan rc = sqlite3session_changeset(pSession, pnChangeset, ppChangeset);
92a87070a2Sdan }
93a87070a2Sdan
94a87070a2Sdan /* Delete the session object */
95a87070a2Sdan sqlite3session_delete(pSession);
96a87070a2Sdan
97a87070a2Sdan return rc;
98a87070a2Sdan }
99a87070a2Sdan /************************************************************************/
100a87070a2Sdan
101a87070a2Sdan /*
102a87070a2Sdan ** Tclcmd: sql_exec_changeset DB SQL
103a87070a2Sdan */
test_sql_exec_changeset(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])104a87070a2Sdan static int SQLITE_TCLAPI test_sql_exec_changeset(
105a87070a2Sdan void * clientData,
106a87070a2Sdan Tcl_Interp *interp,
107a87070a2Sdan int objc,
108a87070a2Sdan Tcl_Obj *CONST objv[]
109a87070a2Sdan ){
110a87070a2Sdan const char *zSql;
111a87070a2Sdan sqlite3 *db;
112a87070a2Sdan void *pChangeset;
113a87070a2Sdan int nChangeset;
114a87070a2Sdan int rc;
115a87070a2Sdan
116a87070a2Sdan if( objc!=3 ){
117a87070a2Sdan Tcl_WrongNumArgs(interp, 1, objv, "DB SQL");
118a87070a2Sdan return TCL_ERROR;
119a87070a2Sdan }
120a87070a2Sdan if( dbHandleFromObj(interp, objv[1], &db) ) return TCL_ERROR;
121a87070a2Sdan zSql = (const char*)Tcl_GetString(objv[2]);
122a87070a2Sdan
123a87070a2Sdan rc = sql_exec_changeset(db, zSql, &nChangeset, &pChangeset);
124a87070a2Sdan if( rc!=SQLITE_OK ){
125a87070a2Sdan Tcl_ResetResult(interp);
126a87070a2Sdan Tcl_AppendResult(interp, "error in sql_exec_changeset()", 0);
127a87070a2Sdan return TCL_ERROR;
128a87070a2Sdan }
129a87070a2Sdan
130a87070a2Sdan Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pChangeset, nChangeset));
131a87070a2Sdan sqlite3_free(pChangeset);
132a87070a2Sdan return TCL_OK;
133a87070a2Sdan }
134a87070a2Sdan
135a87070a2Sdan
136a87070a2Sdan
137ef7a6304Sdan #define SESSION_STREAM_TCL_VAR "sqlite3session_streams"
138ef7a6304Sdan
139ef7a6304Sdan /*
140ef7a6304Sdan ** Attempt to find the global variable zVar within interpreter interp
1414757c658Sdan ** and extract an integer value from it. Return this value.
142ef7a6304Sdan **
143ef7a6304Sdan ** If the named variable cannot be found, or if it cannot be interpreted
1444757c658Sdan ** as a integer, return 0.
145ef7a6304Sdan */
test_tcl_integer(Tcl_Interp * interp,const char * zVar)1464757c658Sdan static int test_tcl_integer(Tcl_Interp *interp, const char *zVar){
147ef7a6304Sdan Tcl_Obj *pObj;
1484757c658Sdan int iVal = 0;
1493ddc3809Sdan Tcl_Obj *pName = Tcl_NewStringObj(zVar, -1);
1503ddc3809Sdan Tcl_IncrRefCount(pName);
1513ddc3809Sdan pObj = Tcl_ObjGetVar2(interp, pName, 0, TCL_GLOBAL_ONLY);
1523ddc3809Sdan Tcl_DecrRefCount(pName);
1534757c658Sdan if( pObj ) Tcl_GetIntFromObj(0, pObj, &iVal);
1544757c658Sdan return iVal;
155ef7a6304Sdan }
156ef7a6304Sdan
test_session_error(Tcl_Interp * interp,int rc,char * zErr)157b9db9099Sdan static int test_session_error(Tcl_Interp *interp, int rc, char *zErr){
158644abec3Sdrh extern const char *sqlite3ErrName(int);
159644abec3Sdrh Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
160b9db9099Sdan if( zErr ){
161b9db9099Sdan Tcl_AppendResult(interp, " - ", zErr, 0);
162b9db9099Sdan sqlite3_free(zErr);
163b9db9099Sdan }
1644fccf43aSdan return TCL_ERROR;
1654fccf43aSdan }
1664fccf43aSdan
test_table_filter(void * pCtx,const char * zTbl)1677531a5a3Sdan static int test_table_filter(void *pCtx, const char *zTbl){
1687531a5a3Sdan TestSession *p = (TestSession*)pCtx;
1697531a5a3Sdan Tcl_Obj *pEval;
1707531a5a3Sdan int rc;
1717531a5a3Sdan int bRes = 0;
1727531a5a3Sdan
1737531a5a3Sdan pEval = Tcl_DuplicateObj(p->pFilterScript);
1747531a5a3Sdan Tcl_IncrRefCount(pEval);
1757531a5a3Sdan rc = Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zTbl, -1));
1767531a5a3Sdan if( rc==TCL_OK ){
1777531a5a3Sdan rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
1787531a5a3Sdan }
1797531a5a3Sdan if( rc==TCL_OK ){
1807531a5a3Sdan rc = Tcl_GetBooleanFromObj(p->interp, Tcl_GetObjResult(p->interp), &bRes);
1817531a5a3Sdan }
1827531a5a3Sdan if( rc!=TCL_OK ){
1837531a5a3Sdan /* printf("error: %s\n", Tcl_GetStringResult(p->interp)); */
1847531a5a3Sdan Tcl_BackgroundError(p->interp);
1857531a5a3Sdan }
1867531a5a3Sdan Tcl_DecrRefCount(pEval);
1877531a5a3Sdan
1887531a5a3Sdan return bRes;
1897531a5a3Sdan }
1907531a5a3Sdan
191ef7a6304Sdan struct TestSessionsBlob {
192ef7a6304Sdan void *p;
193ef7a6304Sdan int n;
194ef7a6304Sdan };
195ef7a6304Sdan typedef struct TestSessionsBlob TestSessionsBlob;
196ef7a6304Sdan
testStreamOutput(void * pCtx,const void * pData,int nData)197e8fa8c96Sdan static int testStreamOutput(
198ef7a6304Sdan void *pCtx,
199ef7a6304Sdan const void *pData,
200ef7a6304Sdan int nData
201ef7a6304Sdan ){
202ef7a6304Sdan TestSessionsBlob *pBlob = (TestSessionsBlob*)pCtx;
203ef7a6304Sdan char *pNew;
204ef7a6304Sdan
205ef7a6304Sdan assert( nData>0 );
206ef7a6304Sdan pNew = (char*)sqlite3_realloc(pBlob->p, pBlob->n + nData);
207ef7a6304Sdan if( pNew==0 ){
208ef7a6304Sdan return SQLITE_NOMEM;
209ef7a6304Sdan }
210ef7a6304Sdan pBlob->p = (void*)pNew;
211ef7a6304Sdan memcpy(&pNew[pBlob->n], pData, nData);
212ef7a6304Sdan pBlob->n += nData;
213ef7a6304Sdan return SQLITE_OK;
214ef7a6304Sdan }
215ef7a6304Sdan
2164fccf43aSdan /*
2174fccf43aSdan ** Tclcmd: $session attach TABLE
2184fccf43aSdan ** $session changeset
2194fccf43aSdan ** $session delete
2204fccf43aSdan ** $session enable BOOL
221f51e5f6cSdan ** $session indirect INTEGER
222ef7a6304Sdan ** $session patchset
2237531a5a3Sdan ** $session table_filter SCRIPT
2244fccf43aSdan */
test_session_cmd(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])225afe18262Smistachkin static int SQLITE_TCLAPI test_session_cmd(
2264fccf43aSdan void *clientData,
2274fccf43aSdan Tcl_Interp *interp,
2284fccf43aSdan int objc,
2294fccf43aSdan Tcl_Obj *CONST objv[]
2304fccf43aSdan ){
2317531a5a3Sdan TestSession *p = (TestSession*)clientData;
2327531a5a3Sdan sqlite3_session *pSession = p->pSession;
23319f7bd3bSdan static struct SessionSubcmd {
2344fccf43aSdan const char *zSub;
2354fccf43aSdan int nArg;
2364fccf43aSdan const char *zMsg;
2374fccf43aSdan int iSub;
2384fccf43aSdan } aSub[] = {
2394fccf43aSdan { "attach", 1, "TABLE", }, /* 0 */
2404fccf43aSdan { "changeset", 0, "", }, /* 1 */
2414fccf43aSdan { "delete", 0, "", }, /* 2 */
242b4480e94Sdan { "enable", 1, "BOOL", }, /* 3 */
243b4480e94Sdan { "indirect", 1, "BOOL", }, /* 4 */
244b69ec348Sdan { "isempty", 0, "", }, /* 5 */
2457531a5a3Sdan { "table_filter", 1, "SCRIPT", }, /* 6 */
24673b3c055Sdan { "patchset", 0, "", }, /* 7 */
247cf8e9144Sdan { "diff", 2, "FROMDB TBL", }, /* 8 */
2480cb735b9Sdan { "memory_used", 0, "", }, /* 9 */
249a23a873fSdan { "changeset_size", 0, "", }, /* 10 */
250*6d29a4feSdan { "object_config_size", 1, "INTEGER", }, /* 11 */
2514fccf43aSdan { 0 }
2524fccf43aSdan };
2534fccf43aSdan int iSub;
2544fccf43aSdan int rc;
2554fccf43aSdan
2564fccf43aSdan if( objc<2 ){
2574fccf43aSdan Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
2584fccf43aSdan return TCL_ERROR;
2594fccf43aSdan }
2604fccf43aSdan rc = Tcl_GetIndexFromObjStruct(interp,
2614fccf43aSdan objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
2624fccf43aSdan );
2634fccf43aSdan if( rc!=TCL_OK ) return rc;
2644fccf43aSdan if( objc!=2+aSub[iSub].nArg ){
2654fccf43aSdan Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
2664fccf43aSdan return TCL_ERROR;
2674fccf43aSdan }
2684fccf43aSdan
2694fccf43aSdan switch( iSub ){
270ff4d0f41Sdan case 0: { /* attach */
271ff4d0f41Sdan char *zArg = Tcl_GetString(objv[2]);
272ff4d0f41Sdan if( zArg[0]=='*' && zArg[1]=='\0' ) zArg = 0;
273ff4d0f41Sdan rc = sqlite3session_attach(pSession, zArg);
2744fccf43aSdan if( rc!=SQLITE_OK ){
275b9db9099Sdan return test_session_error(interp, rc, 0);
2764fccf43aSdan }
2774fccf43aSdan break;
2787531a5a3Sdan }
2794fccf43aSdan
28073b3c055Sdan case 7: /* patchset */
2814fccf43aSdan case 1: { /* changeset */
282ef7a6304Sdan TestSessionsBlob o = {0, 0};
2834757c658Sdan if( test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
284ef7a6304Sdan void *pCtx = (void*)&o;
28573b3c055Sdan if( iSub==7 ){
286f1a08ad8Sdrh rc = sqlite3session_patchset_strm(pSession, testStreamOutput, pCtx);
28773b3c055Sdan }else{
288f1a08ad8Sdrh rc = sqlite3session_changeset_strm(pSession, testStreamOutput, pCtx);
289ef7a6304Sdan }
290ef7a6304Sdan }else{
291ef7a6304Sdan if( iSub==7 ){
292ef7a6304Sdan rc = sqlite3session_patchset(pSession, &o.n, &o.p);
293ef7a6304Sdan }else{
294ef7a6304Sdan rc = sqlite3session_changeset(pSession, &o.n, &o.p);
295ef7a6304Sdan }
29673b3c055Sdan }
2974fccf43aSdan if( rc==SQLITE_OK ){
298ef7a6304Sdan Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(o.p, o.n));
299ef7a6304Sdan }
300ef7a6304Sdan sqlite3_free(o.p);
301ef7a6304Sdan if( rc!=SQLITE_OK ){
302b9db9099Sdan return test_session_error(interp, rc, 0);
3034fccf43aSdan }
3044fccf43aSdan break;
3054fccf43aSdan }
3064fccf43aSdan
3074fccf43aSdan case 2: /* delete */
3084fccf43aSdan Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
3094fccf43aSdan break;
3104fccf43aSdan
3114fccf43aSdan case 3: { /* enable */
3124fccf43aSdan int val;
313e5754eecSdan if( Tcl_GetIntFromObj(interp, objv[2], &val) ) return TCL_ERROR;
3144fccf43aSdan val = sqlite3session_enable(pSession, val);
3154fccf43aSdan Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
3164fccf43aSdan break;
3174fccf43aSdan }
318b4480e94Sdan
319b4480e94Sdan case 4: { /* indirect */
320b4480e94Sdan int val;
321f51e5f6cSdan if( Tcl_GetIntFromObj(interp, objv[2], &val) ) return TCL_ERROR;
322b4480e94Sdan val = sqlite3session_indirect(pSession, val);
323b4480e94Sdan Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
324b4480e94Sdan break;
325b4480e94Sdan }
326b69ec348Sdan
327b69ec348Sdan case 5: { /* isempty */
328b69ec348Sdan int val;
329b69ec348Sdan val = sqlite3session_isempty(pSession);
330b69ec348Sdan Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
331b69ec348Sdan break;
332b69ec348Sdan }
3337531a5a3Sdan
3347531a5a3Sdan case 6: { /* table_filter */
3357531a5a3Sdan if( p->pFilterScript ) Tcl_DecrRefCount(p->pFilterScript);
3367531a5a3Sdan p->interp = interp;
3377531a5a3Sdan p->pFilterScript = Tcl_DuplicateObj(objv[2]);
3387531a5a3Sdan Tcl_IncrRefCount(p->pFilterScript);
3397531a5a3Sdan sqlite3session_table_filter(pSession, test_table_filter, clientData);
3407531a5a3Sdan break;
3417531a5a3Sdan }
342cf8e9144Sdan
343cf8e9144Sdan case 8: { /* diff */
344cf8e9144Sdan char *zErr = 0;
345cf8e9144Sdan rc = sqlite3session_diff(pSession,
346cf8e9144Sdan Tcl_GetString(objv[2]),
347cf8e9144Sdan Tcl_GetString(objv[3]),
348cf8e9144Sdan &zErr
349cf8e9144Sdan );
350cf8e9144Sdan assert( rc!=SQLITE_OK || zErr==0 );
351cf8e9144Sdan if( rc ){
352b9db9099Sdan return test_session_error(interp, rc, zErr);
353cf8e9144Sdan }
354cf8e9144Sdan break;
355cf8e9144Sdan }
3560cb735b9Sdan
3570cb735b9Sdan case 9: { /* memory_used */
3580cb735b9Sdan sqlite3_int64 nMalloc = sqlite3session_memory_used(pSession);
3590cb735b9Sdan Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nMalloc));
3600cb735b9Sdan break;
3610cb735b9Sdan }
362a23a873fSdan
363a23a873fSdan case 10: {
364a23a873fSdan sqlite3_int64 nSize = sqlite3session_changeset_size(pSession);
365a23a873fSdan Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nSize));
366a23a873fSdan break;
367a23a873fSdan }
368*6d29a4feSdan case 11: {
369*6d29a4feSdan int rc;
370*6d29a4feSdan int iArg;
371*6d29a4feSdan if( Tcl_GetIntFromObj(interp, objv[2], &iArg) ){
372*6d29a4feSdan return TCL_ERROR;
373*6d29a4feSdan }
374*6d29a4feSdan rc = sqlite3session_object_config(
375*6d29a4feSdan pSession, SQLITE_SESSION_OBJCONFIG_SIZE, &iArg
376*6d29a4feSdan );
377*6d29a4feSdan if( rc!=SQLITE_OK ){
378*6d29a4feSdan extern const char *sqlite3ErrName(int);
379*6d29a4feSdan Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
380*6d29a4feSdan }else{
381*6d29a4feSdan Tcl_SetObjResult(interp, Tcl_NewIntObj(iArg));
382*6d29a4feSdan }
383*6d29a4feSdan break;
384*6d29a4feSdan }
3854fccf43aSdan }
3864fccf43aSdan
3874fccf43aSdan return TCL_OK;
3884fccf43aSdan }
3894fccf43aSdan
test_session_del(void * clientData)3902d45d7bfSmistachkin static void SQLITE_TCLAPI test_session_del(void *clientData){
3917531a5a3Sdan TestSession *p = (TestSession*)clientData;
3927531a5a3Sdan if( p->pFilterScript ) Tcl_DecrRefCount(p->pFilterScript);
3937531a5a3Sdan sqlite3session_delete(p->pSession);
394b7bc8c98Sdrh ckfree((char*)p);
3954fccf43aSdan }
3964fccf43aSdan
3974fccf43aSdan /*
3984fccf43aSdan ** Tclcmd: sqlite3session CMD DB-HANDLE DB-NAME
3994fccf43aSdan */
test_sqlite3session(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])400afe18262Smistachkin static int SQLITE_TCLAPI test_sqlite3session(
4014fccf43aSdan void * clientData,
4024fccf43aSdan Tcl_Interp *interp,
4034fccf43aSdan int objc,
4044fccf43aSdan Tcl_Obj *CONST objv[]
4054fccf43aSdan ){
4064fccf43aSdan sqlite3 *db;
4074fccf43aSdan Tcl_CmdInfo info;
4084fccf43aSdan int rc; /* sqlite3session_create() return code */
4097531a5a3Sdan TestSession *p; /* New wrapper object */
410*6d29a4feSdan int iArg = -1;
4114fccf43aSdan
4124fccf43aSdan if( objc!=4 ){
4134fccf43aSdan Tcl_WrongNumArgs(interp, 1, objv, "CMD DB-HANDLE DB-NAME");
4144fccf43aSdan return TCL_ERROR;
4154fccf43aSdan }
4164fccf43aSdan
4174fccf43aSdan if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[2]), &info) ){
4184fccf43aSdan Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0);
4194fccf43aSdan return TCL_ERROR;
4204fccf43aSdan }
4214fccf43aSdan db = *(sqlite3 **)info.objClientData;
4224fccf43aSdan
4237531a5a3Sdan p = (TestSession*)ckalloc(sizeof(TestSession));
4247531a5a3Sdan memset(p, 0, sizeof(TestSession));
4257531a5a3Sdan rc = sqlite3session_create(db, Tcl_GetString(objv[3]), &p->pSession);
4264fccf43aSdan if( rc!=SQLITE_OK ){
427b7bc8c98Sdrh ckfree((char*)p);
428b9db9099Sdan return test_session_error(interp, rc, 0);
4294fccf43aSdan }
4304fccf43aSdan
431*6d29a4feSdan /* Query the SQLITE_SESSION_OBJCONFIG_SIZE option to ensure that it
432*6d29a4feSdan ** is clear by default. Then set it. */
433*6d29a4feSdan sqlite3session_object_config(p->pSession,SQLITE_SESSION_OBJCONFIG_SIZE,&iArg);
434*6d29a4feSdan assert( iArg==0 );
435*6d29a4feSdan iArg = 1;
436*6d29a4feSdan sqlite3session_object_config(p->pSession,SQLITE_SESSION_OBJCONFIG_SIZE,&iArg);
437*6d29a4feSdan
4384fccf43aSdan Tcl_CreateObjCommand(
4397531a5a3Sdan interp, Tcl_GetString(objv[1]), test_session_cmd, (ClientData)p,
4404fccf43aSdan test_session_del
4414fccf43aSdan );
4424fccf43aSdan Tcl_SetObjResult(interp, objv[1]);
4434fccf43aSdan return TCL_OK;
4444fccf43aSdan }
4454fccf43aSdan
test_append_value(Tcl_Obj * pList,sqlite3_value * pVal)4464fccf43aSdan static void test_append_value(Tcl_Obj *pList, sqlite3_value *pVal){
4474fccf43aSdan if( pVal==0 ){
4484fccf43aSdan Tcl_ListObjAppendElement(0, pList, Tcl_NewObj());
4494fccf43aSdan Tcl_ListObjAppendElement(0, pList, Tcl_NewObj());
4504fccf43aSdan }else{
4514fccf43aSdan Tcl_Obj *pObj;
4524fccf43aSdan switch( sqlite3_value_type(pVal) ){
4534fccf43aSdan case SQLITE_NULL:
4544fccf43aSdan Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("n", 1));
4554fccf43aSdan pObj = Tcl_NewObj();
4564fccf43aSdan break;
4574fccf43aSdan case SQLITE_INTEGER:
4584fccf43aSdan Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("i", 1));
4594fccf43aSdan pObj = Tcl_NewWideIntObj(sqlite3_value_int64(pVal));
4604fccf43aSdan break;
4614fccf43aSdan case SQLITE_FLOAT:
4624fccf43aSdan Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("f", 1));
4634fccf43aSdan pObj = Tcl_NewDoubleObj(sqlite3_value_double(pVal));
4644fccf43aSdan break;
465b7bc8c98Sdrh case SQLITE_TEXT: {
466b7bc8c98Sdrh const char *z = (char*)sqlite3_value_blob(pVal);
467b7bc8c98Sdrh int n = sqlite3_value_bytes(pVal);
4684fccf43aSdan Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("t", 1));
469b7bc8c98Sdrh pObj = Tcl_NewStringObj(z, n);
4704fccf43aSdan break;
471b7bc8c98Sdrh }
472c5896b5cSmistachkin default:
473c5896b5cSmistachkin assert( sqlite3_value_type(pVal)==SQLITE_BLOB );
4744fccf43aSdan Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("b", 1));
4754fccf43aSdan pObj = Tcl_NewByteArrayObj(
4764fccf43aSdan sqlite3_value_blob(pVal),
4774fccf43aSdan sqlite3_value_bytes(pVal)
4784fccf43aSdan );
4794fccf43aSdan break;
4804fccf43aSdan }
4814fccf43aSdan Tcl_ListObjAppendElement(0, pList, pObj);
4824fccf43aSdan }
4834fccf43aSdan }
4844fccf43aSdan
485d5f0767cSdan typedef struct TestConflictHandler TestConflictHandler;
486d5f0767cSdan struct TestConflictHandler {
487d5f0767cSdan Tcl_Interp *interp;
48840368988Sdan Tcl_Obj *pConflictScript;
48940368988Sdan Tcl_Obj *pFilterScript;
490d5f0767cSdan };
491d5f0767cSdan
test_obj_eq_string(Tcl_Obj * p,const char * z)4920c698471Sdan static int test_obj_eq_string(Tcl_Obj *p, const char *z){
4930c698471Sdan int n;
4940c698471Sdan int nObj;
4950c698471Sdan char *zObj;
4960c698471Sdan
49787778c7fSdrh n = (int)strlen(z);
4980c698471Sdan zObj = Tcl_GetStringFromObj(p, &nObj);
4990c698471Sdan
5000c698471Sdan return (nObj==n && (n==0 || 0==memcmp(zObj, z, n)));
5010c698471Sdan }
5020c698471Sdan
test_filter_handler(void * pCtx,const char * zTab)50340368988Sdan static int test_filter_handler(
50440368988Sdan void *pCtx, /* Pointer to TestConflictHandler structure */
50540368988Sdan const char *zTab /* Table name */
50640368988Sdan ){
50740368988Sdan TestConflictHandler *p = (TestConflictHandler *)pCtx;
50840368988Sdan int res = 1;
50940368988Sdan Tcl_Obj *pEval;
51040368988Sdan Tcl_Interp *interp = p->interp;
51140368988Sdan
51240368988Sdan pEval = Tcl_DuplicateObj(p->pFilterScript);
51340368988Sdan Tcl_IncrRefCount(pEval);
51440368988Sdan
51540368988Sdan if( TCL_OK!=Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zTab, -1))
51640368988Sdan || TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL)
51740368988Sdan || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &res)
51840368988Sdan ){
51940368988Sdan Tcl_BackgroundError(interp);
52040368988Sdan }
52140368988Sdan
52240368988Sdan Tcl_DecrRefCount(pEval);
52340368988Sdan return res;
52440368988Sdan }
52540368988Sdan
test_conflict_handler(void * pCtx,int eConf,sqlite3_changeset_iter * pIter)526d5f0767cSdan static int test_conflict_handler(
527d5f0767cSdan void *pCtx, /* Pointer to TestConflictHandler structure */
528d5f0767cSdan int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */
529d5f0767cSdan sqlite3_changeset_iter *pIter /* Handle describing change and conflict */
530d5f0767cSdan ){
531d5f0767cSdan TestConflictHandler *p = (TestConflictHandler *)pCtx;
532d5f0767cSdan Tcl_Obj *pEval;
533d5f0767cSdan Tcl_Interp *interp = p->interp;
5340c698471Sdan int ret = 0; /* Return value */
535d5f0767cSdan
536d5f0767cSdan int op; /* SQLITE_UPDATE, DELETE or INSERT */
537d5f0767cSdan const char *zTab; /* Name of table conflict is on */
538d5f0767cSdan int nCol; /* Number of columns in table zTab */
539d5f0767cSdan
54040368988Sdan pEval = Tcl_DuplicateObj(p->pConflictScript);
541d5f0767cSdan Tcl_IncrRefCount(pEval);
542d5f0767cSdan
543b4480e94Sdan sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
544d5f0767cSdan
545cb3e4b79Sdan if( eConf==SQLITE_CHANGESET_FOREIGN_KEY ){
546cb3e4b79Sdan int nFk;
547cb3e4b79Sdan sqlite3changeset_fk_conflicts(pIter, &nFk);
548cb3e4b79Sdan Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj("FOREIGN_KEY", -1));
549cb3e4b79Sdan Tcl_ListObjAppendElement(0, pEval, Tcl_NewIntObj(nFk));
550cb3e4b79Sdan }else{
551cb3e4b79Sdan
552d5f0767cSdan /* Append the operation type. */
553d5f0767cSdan Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(
554d5f0767cSdan op==SQLITE_INSERT ? "INSERT" :
555d5f0767cSdan op==SQLITE_UPDATE ? "UPDATE" :
556d5f0767cSdan "DELETE", -1
557d5f0767cSdan ));
558d5f0767cSdan
559d5f0767cSdan /* Append the table name. */
560d5f0767cSdan Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zTab, -1));
561d5f0767cSdan
562d5f0767cSdan /* Append the conflict type. */
563d5f0767cSdan switch( eConf ){
564d5f0767cSdan case SQLITE_CHANGESET_DATA:
565d5f0767cSdan Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("DATA",-1));
566d5f0767cSdan break;
567d5f0767cSdan case SQLITE_CHANGESET_NOTFOUND:
568d5f0767cSdan Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("NOTFOUND",-1));
569d5f0767cSdan break;
570d5f0767cSdan case SQLITE_CHANGESET_CONFLICT:
571d5f0767cSdan Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("CONFLICT",-1));
572d5f0767cSdan break;
573d5f0767cSdan case SQLITE_CHANGESET_CONSTRAINT:
574d5f0767cSdan Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("CONSTRAINT",-1));
575d5f0767cSdan break;
576d5f0767cSdan }
577d5f0767cSdan
578d5f0767cSdan /* If this is not an INSERT, append the old row */
579d5f0767cSdan if( op!=SQLITE_INSERT ){
580d5f0767cSdan int i;
581d5f0767cSdan Tcl_Obj *pOld = Tcl_NewObj();
582d5f0767cSdan for(i=0; i<nCol; i++){
583d5f0767cSdan sqlite3_value *pVal;
584d5f0767cSdan sqlite3changeset_old(pIter, i, &pVal);
585d5f0767cSdan test_append_value(pOld, pVal);
586d5f0767cSdan }
587d5f0767cSdan Tcl_ListObjAppendElement(0, pEval, pOld);
588d5f0767cSdan }
589d5f0767cSdan
590d5f0767cSdan /* If this is not a DELETE, append the new row */
591d5f0767cSdan if( op!=SQLITE_DELETE ){
592d5f0767cSdan int i;
593d5f0767cSdan Tcl_Obj *pNew = Tcl_NewObj();
594d5f0767cSdan for(i=0; i<nCol; i++){
595d5f0767cSdan sqlite3_value *pVal;
596d5f0767cSdan sqlite3changeset_new(pIter, i, &pVal);
597d5f0767cSdan test_append_value(pNew, pVal);
598d5f0767cSdan }
599d5f0767cSdan Tcl_ListObjAppendElement(0, pEval, pNew);
600d5f0767cSdan }
601d5f0767cSdan
602d5f0767cSdan /* If this is a CHANGESET_DATA or CHANGESET_CONFLICT conflict, append
603d5f0767cSdan ** the conflicting row. */
604d5f0767cSdan if( eConf==SQLITE_CHANGESET_DATA || eConf==SQLITE_CHANGESET_CONFLICT ){
605d5f0767cSdan int i;
606d5f0767cSdan Tcl_Obj *pConflict = Tcl_NewObj();
607d5f0767cSdan for(i=0; i<nCol; i++){
608f51e5f6cSdan int rc;
609d5f0767cSdan sqlite3_value *pVal;
610f51e5f6cSdan rc = sqlite3changeset_conflict(pIter, i, &pVal);
611f51e5f6cSdan assert( rc==SQLITE_OK );
612d5f0767cSdan test_append_value(pConflict, pVal);
613d5f0767cSdan }
614d5f0767cSdan Tcl_ListObjAppendElement(0, pEval, pConflict);
615d5f0767cSdan }
616d5f0767cSdan
617f51e5f6cSdan /***********************************************************************
618f51e5f6cSdan ** This block is purely for testing some error conditions.
619f51e5f6cSdan */
620cb3e4b79Sdan if( eConf==SQLITE_CHANGESET_CONSTRAINT
621cb3e4b79Sdan || eConf==SQLITE_CHANGESET_NOTFOUND
622cb3e4b79Sdan ){
623f51e5f6cSdan sqlite3_value *pVal;
624f51e5f6cSdan int rc = sqlite3changeset_conflict(pIter, 0, &pVal);
625f51e5f6cSdan assert( rc==SQLITE_MISUSE );
626f51e5f6cSdan }else{
627f51e5f6cSdan sqlite3_value *pVal;
628f51e5f6cSdan int rc = sqlite3changeset_conflict(pIter, -1, &pVal);
629f51e5f6cSdan assert( rc==SQLITE_RANGE );
630f51e5f6cSdan rc = sqlite3changeset_conflict(pIter, nCol, &pVal);
631f51e5f6cSdan assert( rc==SQLITE_RANGE );
632f51e5f6cSdan }
633f51e5f6cSdan if( op==SQLITE_DELETE ){
634f51e5f6cSdan sqlite3_value *pVal;
635f51e5f6cSdan int rc = sqlite3changeset_new(pIter, 0, &pVal);
636f51e5f6cSdan assert( rc==SQLITE_MISUSE );
637f51e5f6cSdan }else{
638f51e5f6cSdan sqlite3_value *pVal;
639f51e5f6cSdan int rc = sqlite3changeset_new(pIter, -1, &pVal);
640f51e5f6cSdan assert( rc==SQLITE_RANGE );
641f51e5f6cSdan rc = sqlite3changeset_new(pIter, nCol, &pVal);
642f51e5f6cSdan assert( rc==SQLITE_RANGE );
643f51e5f6cSdan }
644f51e5f6cSdan if( op==SQLITE_INSERT ){
645f51e5f6cSdan sqlite3_value *pVal;
646f51e5f6cSdan int rc = sqlite3changeset_old(pIter, 0, &pVal);
647f51e5f6cSdan assert( rc==SQLITE_MISUSE );
648f51e5f6cSdan }else{
649f51e5f6cSdan sqlite3_value *pVal;
650f51e5f6cSdan int rc = sqlite3changeset_old(pIter, -1, &pVal);
651f51e5f6cSdan assert( rc==SQLITE_RANGE );
652f51e5f6cSdan rc = sqlite3changeset_old(pIter, nCol, &pVal);
653f51e5f6cSdan assert( rc==SQLITE_RANGE );
654f51e5f6cSdan }
655082c96dfSdan if( eConf!=SQLITE_CHANGESET_FOREIGN_KEY ){
656082c96dfSdan /* eConf!=FOREIGN_KEY is always true at this point. The condition is
657082c96dfSdan ** just there to make it clearer what is being tested. */
658082c96dfSdan int nDummy;
659082c96dfSdan int rc = sqlite3changeset_fk_conflicts(pIter, &nDummy);
660082c96dfSdan assert( rc==SQLITE_MISUSE );
661082c96dfSdan }
662f51e5f6cSdan /* End of testing block
663f51e5f6cSdan ***********************************************************************/
664cb3e4b79Sdan }
665f51e5f6cSdan
666d5f0767cSdan if( TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL) ){
667d5f0767cSdan Tcl_BackgroundError(interp);
6680c698471Sdan }else{
6690c698471Sdan Tcl_Obj *pRes = Tcl_GetObjResult(interp);
6700c698471Sdan if( test_obj_eq_string(pRes, "OMIT") || test_obj_eq_string(pRes, "") ){
6710c698471Sdan ret = SQLITE_CHANGESET_OMIT;
6720c698471Sdan }else if( test_obj_eq_string(pRes, "REPLACE") ){
6730c698471Sdan ret = SQLITE_CHANGESET_REPLACE;
6740c698471Sdan }else if( test_obj_eq_string(pRes, "ABORT") ){
6750c698471Sdan ret = SQLITE_CHANGESET_ABORT;
6760c698471Sdan }else{
677f51e5f6cSdan Tcl_GetIntFromObj(0, pRes, &ret);
678d5f0767cSdan }
6790c698471Sdan }
6800c698471Sdan
681d5f0767cSdan Tcl_DecrRefCount(pEval);
6820c698471Sdan return ret;
683d5f0767cSdan }
684d5f0767cSdan
685d5f0767cSdan /*
686082c96dfSdan ** The conflict handler used by sqlite3changeset_apply_replace_all().
687082c96dfSdan ** This conflict handler calls sqlite3_value_text16() on all available
688082c96dfSdan ** sqlite3_value objects and then returns CHANGESET_REPLACE, or
689082c96dfSdan ** CHANGESET_OMIT if REPLACE is not applicable. This is used to test the
690082c96dfSdan ** effect of a malloc failure within an sqlite3_value_xxx() function
691082c96dfSdan ** invoked by a conflict-handler callback.
692082c96dfSdan */
replace_handler(void * pCtx,int eConf,sqlite3_changeset_iter * pIter)693082c96dfSdan static int replace_handler(
694082c96dfSdan void *pCtx, /* Pointer to TestConflictHandler structure */
695082c96dfSdan int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */
696082c96dfSdan sqlite3_changeset_iter *pIter /* Handle describing change and conflict */
697082c96dfSdan ){
698082c96dfSdan int op; /* SQLITE_UPDATE, DELETE or INSERT */
699082c96dfSdan const char *zTab; /* Name of table conflict is on */
700082c96dfSdan int nCol; /* Number of columns in table zTab */
701082c96dfSdan int i;
702082c96dfSdan int x = 0;
703082c96dfSdan
704082c96dfSdan sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
705082c96dfSdan
706082c96dfSdan if( op!=SQLITE_INSERT ){
707082c96dfSdan for(i=0; i<nCol; i++){
708082c96dfSdan sqlite3_value *pVal;
709082c96dfSdan sqlite3changeset_old(pIter, i, &pVal);
710082c96dfSdan sqlite3_value_text16(pVal);
711082c96dfSdan x++;
712082c96dfSdan }
713082c96dfSdan }
714082c96dfSdan
715082c96dfSdan if( op!=SQLITE_DELETE ){
716082c96dfSdan for(i=0; i<nCol; i++){
717082c96dfSdan sqlite3_value *pVal;
718082c96dfSdan sqlite3changeset_new(pIter, i, &pVal);
719082c96dfSdan sqlite3_value_text16(pVal);
720082c96dfSdan x++;
721082c96dfSdan }
722082c96dfSdan }
723082c96dfSdan
724082c96dfSdan if( eConf==SQLITE_CHANGESET_DATA ){
725082c96dfSdan return SQLITE_CHANGESET_REPLACE;
726082c96dfSdan }
727082c96dfSdan return SQLITE_CHANGESET_OMIT;
728082c96dfSdan }
729082c96dfSdan
testStreamInput(void * pCtx,void * pData,int * pnData)7304757c658Sdan static int testStreamInput(
7314757c658Sdan void *pCtx, /* Context pointer */
7324757c658Sdan void *pData, /* Buffer to populate */
7334757c658Sdan int *pnData /* IN/OUT: Bytes requested/supplied */
7344757c658Sdan ){
7354757c658Sdan TestStreamInput *p = (TestStreamInput*)pCtx;
7364757c658Sdan int nReq = *pnData; /* Bytes of data requested */
7374757c658Sdan int nRem = p->nData - p->iData; /* Bytes of data available */
7384757c658Sdan int nRet = p->nStream; /* Bytes actually returned */
7394757c658Sdan
740e8fa8c96Sdan /* Allocate and free some space. There is no point to this, other than
741e8fa8c96Sdan ** that it allows the regular OOM fault-injection tests to cause an error
742e8fa8c96Sdan ** in this function. */
743e8fa8c96Sdan void *pAlloc = sqlite3_malloc(10);
744e8fa8c96Sdan if( pAlloc==0 ) return SQLITE_NOMEM;
745e8fa8c96Sdan sqlite3_free(pAlloc);
746e8fa8c96Sdan
7474757c658Sdan if( nRet>nReq ) nRet = nReq;
7484757c658Sdan if( nRet>nRem ) nRet = nRem;
7494757c658Sdan
7504757c658Sdan assert( nRet>=0 );
7514757c658Sdan if( nRet>0 ){
7524757c658Sdan memcpy(pData, &p->aData[p->iData], nRet);
7534757c658Sdan p->iData += nRet;
7544757c658Sdan }
7554757c658Sdan
7564757c658Sdan *pnData = nRet;
7574757c658Sdan return SQLITE_OK;
7584757c658Sdan }
7594757c658Sdan
7604757c658Sdan
testSqlite3changesetApply(int bV2,void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])761a38e6c57Sdan static int SQLITE_TCLAPI testSqlite3changesetApply(
762a38e6c57Sdan int bV2,
763d5f0767cSdan void * clientData,
764d5f0767cSdan Tcl_Interp *interp,
765d5f0767cSdan int objc,
766d5f0767cSdan Tcl_Obj *CONST objv[]
767d5f0767cSdan ){
768d5f0767cSdan sqlite3 *db; /* Database handle */
769d5f0767cSdan Tcl_CmdInfo info; /* Database Tcl command (objv[1]) info */
770d5f0767cSdan int rc; /* Return code from changeset_invert() */
771d5f0767cSdan void *pChangeset; /* Buffer containing changeset */
772d5f0767cSdan int nChangeset; /* Size of buffer aChangeset in bytes */
773d5f0767cSdan TestConflictHandler ctx;
7744757c658Sdan TestStreamInput sStr;
775a38e6c57Sdan void *pRebase = 0;
776a38e6c57Sdan int nRebase = 0;
777fe55da38Sdan int flags = 0; /* Flags for apply_v2() */
7784757c658Sdan
7794757c658Sdan memset(&sStr, 0, sizeof(sStr));
7804757c658Sdan sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
781d5f0767cSdan
782fe55da38Sdan /* Check for the -nosavepoint flag */
78344748f27Sdan if( bV2 ){
78444748f27Sdan if( objc>1 ){
785fe55da38Sdan const char *z1 = Tcl_GetString(objv[1]);
786fe55da38Sdan int n = strlen(z1);
787fe55da38Sdan if( n>1 && n<=12 && 0==sqlite3_strnicmp("-nosavepoint", z1, n) ){
78844748f27Sdan flags |= SQLITE_CHANGESETAPPLY_NOSAVEPOINT;
789fe55da38Sdan objc--;
790fe55da38Sdan objv++;
791fe55da38Sdan }
792fe55da38Sdan }
79344748f27Sdan if( objc>1 ){
79444748f27Sdan const char *z1 = Tcl_GetString(objv[1]);
79544748f27Sdan int n = strlen(z1);
79644748f27Sdan if( n>1 && n<=7 && 0==sqlite3_strnicmp("-invert", z1, n) ){
79744748f27Sdan flags |= SQLITE_CHANGESETAPPLY_INVERT;
79844748f27Sdan objc--;
79944748f27Sdan objv++;
80044748f27Sdan }
80144748f27Sdan }
80244748f27Sdan }
803fe55da38Sdan
80440368988Sdan if( objc!=4 && objc!=5 ){
805fe55da38Sdan const char *zMsg;
806fe55da38Sdan if( bV2 ){
80744748f27Sdan zMsg = "?-nosavepoint? ?-inverse? "
80844748f27Sdan "DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?";
809fe55da38Sdan }else{
810fe55da38Sdan zMsg = "DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?";
811fe55da38Sdan }
812fe55da38Sdan Tcl_WrongNumArgs(interp, 1, objv, zMsg);
813d5f0767cSdan return TCL_ERROR;
814d5f0767cSdan }
815d5f0767cSdan if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &info) ){
816fe55da38Sdan Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[1]), 0);
817d5f0767cSdan return TCL_ERROR;
818d5f0767cSdan }
819d5f0767cSdan db = *(sqlite3 **)info.objClientData;
820d5f0767cSdan pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset);
82140368988Sdan ctx.pConflictScript = objv[3];
82240368988Sdan ctx.pFilterScript = objc==5 ? objv[4] : 0;
823d5f0767cSdan ctx.interp = interp;
824d5f0767cSdan
8254757c658Sdan if( sStr.nStream==0 ){
826a38e6c57Sdan if( bV2==0 ){
82740368988Sdan rc = sqlite3changeset_apply(db, nChangeset, pChangeset,
82840368988Sdan (objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx
829d5f0767cSdan );
8304757c658Sdan }else{
831a38e6c57Sdan rc = sqlite3changeset_apply_v2(db, nChangeset, pChangeset,
832a38e6c57Sdan (objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx,
833fe55da38Sdan &pRebase, &nRebase, flags
834a38e6c57Sdan );
835a38e6c57Sdan }
836a38e6c57Sdan }else{
8374757c658Sdan sStr.aData = (unsigned char*)pChangeset;
8384757c658Sdan sStr.nData = nChangeset;
839c0a499eaSdan if( bV2==0 ){
840f1a08ad8Sdrh rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
841c0a499eaSdan (objc==5) ? test_filter_handler : 0,
842c0a499eaSdan test_conflict_handler, (void *)&ctx
8434757c658Sdan );
844c0a499eaSdan }else{
845c0a499eaSdan rc = sqlite3changeset_apply_v2_strm(db, testStreamInput, (void*)&sStr,
846c0a499eaSdan (objc==5) ? test_filter_handler : 0,
847c0a499eaSdan test_conflict_handler, (void *)&ctx,
848fe55da38Sdan &pRebase, &nRebase, flags
849c0a499eaSdan );
850c0a499eaSdan }
8514757c658Sdan }
8524757c658Sdan
853d5f0767cSdan if( rc!=SQLITE_OK ){
854b9db9099Sdan return test_session_error(interp, rc, 0);
855a38e6c57Sdan }else{
856d5f0767cSdan Tcl_ResetResult(interp);
857a38e6c57Sdan if( bV2 && pRebase ){
858a38e6c57Sdan Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pRebase, nRebase));
859a38e6c57Sdan }
860a38e6c57Sdan }
861a38e6c57Sdan sqlite3_free(pRebase);
862d5f0767cSdan return TCL_OK;
863d5f0767cSdan }
864d5f0767cSdan
8654fccf43aSdan /*
866a38e6c57Sdan ** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
867a38e6c57Sdan */
test_sqlite3changeset_apply(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])868a38e6c57Sdan static int SQLITE_TCLAPI test_sqlite3changeset_apply(
869a38e6c57Sdan void * clientData,
870a38e6c57Sdan Tcl_Interp *interp,
871a38e6c57Sdan int objc,
872a38e6c57Sdan Tcl_Obj *CONST objv[]
873a38e6c57Sdan ){
874a38e6c57Sdan return testSqlite3changesetApply(0, clientData, interp, objc, objv);
875a38e6c57Sdan }
876a38e6c57Sdan /*
877a38e6c57Sdan ** sqlite3changeset_apply_v2 DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
878a38e6c57Sdan */
test_sqlite3changeset_apply_v2(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])879a38e6c57Sdan static int SQLITE_TCLAPI test_sqlite3changeset_apply_v2(
880a38e6c57Sdan void * clientData,
881a38e6c57Sdan Tcl_Interp *interp,
882a38e6c57Sdan int objc,
883a38e6c57Sdan Tcl_Obj *CONST objv[]
884a38e6c57Sdan ){
885a38e6c57Sdan return testSqlite3changesetApply(1, clientData, interp, objc, objv);
886a38e6c57Sdan }
887a38e6c57Sdan
888a38e6c57Sdan /*
889082c96dfSdan ** sqlite3changeset_apply_replace_all DB CHANGESET
890082c96dfSdan */
test_sqlite3changeset_apply_replace_all(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])891afe18262Smistachkin static int SQLITE_TCLAPI test_sqlite3changeset_apply_replace_all(
892082c96dfSdan void * clientData,
893082c96dfSdan Tcl_Interp *interp,
894082c96dfSdan int objc,
895082c96dfSdan Tcl_Obj *CONST objv[]
896082c96dfSdan ){
897082c96dfSdan sqlite3 *db; /* Database handle */
898082c96dfSdan Tcl_CmdInfo info; /* Database Tcl command (objv[1]) info */
899082c96dfSdan int rc; /* Return code from changeset_invert() */
900082c96dfSdan void *pChangeset; /* Buffer containing changeset */
901082c96dfSdan int nChangeset; /* Size of buffer aChangeset in bytes */
902082c96dfSdan
903082c96dfSdan if( objc!=3 ){
904082c96dfSdan Tcl_WrongNumArgs(interp, 1, objv, "DB CHANGESET");
905082c96dfSdan return TCL_ERROR;
906082c96dfSdan }
907082c96dfSdan if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &info) ){
908082c96dfSdan Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0);
909082c96dfSdan return TCL_ERROR;
910082c96dfSdan }
911082c96dfSdan db = *(sqlite3 **)info.objClientData;
912082c96dfSdan pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset);
913082c96dfSdan
914082c96dfSdan rc = sqlite3changeset_apply(db, nChangeset, pChangeset, 0, replace_handler,0);
915082c96dfSdan if( rc!=SQLITE_OK ){
916b9db9099Sdan return test_session_error(interp, rc, 0);
917082c96dfSdan }
918082c96dfSdan Tcl_ResetResult(interp);
919082c96dfSdan return TCL_OK;
920082c96dfSdan }
921082c96dfSdan
922082c96dfSdan
923082c96dfSdan /*
92491ddd559Sdan ** sqlite3changeset_invert CHANGESET
92591ddd559Sdan */
test_sqlite3changeset_invert(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])926afe18262Smistachkin static int SQLITE_TCLAPI test_sqlite3changeset_invert(
92791ddd559Sdan void * clientData,
92891ddd559Sdan Tcl_Interp *interp,
92991ddd559Sdan int objc,
93091ddd559Sdan Tcl_Obj *CONST objv[]
93191ddd559Sdan ){
93291ddd559Sdan int rc; /* Return code from changeset_invert() */
933fa122adaSdan TestStreamInput sIn; /* Input stream */
934fa122adaSdan TestSessionsBlob sOut; /* Output blob */
93591ddd559Sdan
93691ddd559Sdan if( objc!=2 ){
93791ddd559Sdan Tcl_WrongNumArgs(interp, 1, objv, "CHANGESET");
938d5f0767cSdan return TCL_ERROR;
93991ddd559Sdan }
94091ddd559Sdan
941fa122adaSdan memset(&sIn, 0, sizeof(sIn));
942fa122adaSdan memset(&sOut, 0, sizeof(sOut));
943fa122adaSdan sIn.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
944fa122adaSdan sIn.aData = Tcl_GetByteArrayFromObj(objv[1], &sIn.nData);
945fa122adaSdan
946fa122adaSdan if( sIn.nStream ){
947f1a08ad8Sdrh rc = sqlite3changeset_invert_strm(
948e8fa8c96Sdan testStreamInput, (void*)&sIn, testStreamOutput, (void*)&sOut
949fa122adaSdan );
950fa122adaSdan }else{
951fa122adaSdan rc = sqlite3changeset_invert(sIn.nData, sIn.aData, &sOut.n, &sOut.p);
95291ddd559Sdan }
953fa122adaSdan if( rc!=SQLITE_OK ){
954b9db9099Sdan rc = test_session_error(interp, rc, 0);
955fa122adaSdan }else{
956fa122adaSdan Tcl_SetObjResult(interp,Tcl_NewByteArrayObj((unsigned char*)sOut.p,sOut.n));
957fa122adaSdan }
958fa122adaSdan sqlite3_free(sOut.p);
959fa122adaSdan return rc;
96091ddd559Sdan }
96191ddd559Sdan
96291ddd559Sdan /*
9635d607a6eSdan ** sqlite3changeset_concat LEFT RIGHT
9645d607a6eSdan */
test_sqlite3changeset_concat(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])965afe18262Smistachkin static int SQLITE_TCLAPI test_sqlite3changeset_concat(
9665d607a6eSdan void * clientData,
9675d607a6eSdan Tcl_Interp *interp,
9685d607a6eSdan int objc,
9695d607a6eSdan Tcl_Obj *CONST objv[]
9705d607a6eSdan ){
9715d607a6eSdan int rc; /* Return code from changeset_invert() */
972cbf6d2d2Sdan
973cbf6d2d2Sdan TestStreamInput sLeft; /* Input stream */
974cbf6d2d2Sdan TestStreamInput sRight; /* Input stream */
975cbf6d2d2Sdan TestSessionsBlob sOut = {0,0}; /* Output blob */
9765d607a6eSdan
9775d607a6eSdan if( objc!=3 ){
9785d607a6eSdan Tcl_WrongNumArgs(interp, 1, objv, "LEFT RIGHT");
9795d607a6eSdan return TCL_ERROR;
9805d607a6eSdan }
9815d607a6eSdan
982cbf6d2d2Sdan memset(&sLeft, 0, sizeof(sLeft));
983cbf6d2d2Sdan memset(&sRight, 0, sizeof(sRight));
984cbf6d2d2Sdan sLeft.aData = Tcl_GetByteArrayFromObj(objv[1], &sLeft.nData);
985cbf6d2d2Sdan sRight.aData = Tcl_GetByteArrayFromObj(objv[2], &sRight.nData);
986cbf6d2d2Sdan sLeft.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
987cbf6d2d2Sdan sRight.nStream = sLeft.nStream;
988cbf6d2d2Sdan
989cbf6d2d2Sdan if( sLeft.nStream>0 ){
990f1a08ad8Sdrh rc = sqlite3changeset_concat_strm(
991cbf6d2d2Sdan testStreamInput, (void*)&sLeft,
992cbf6d2d2Sdan testStreamInput, (void*)&sRight,
993e8fa8c96Sdan testStreamOutput, (void*)&sOut
994cbf6d2d2Sdan );
995cbf6d2d2Sdan }else{
996cbf6d2d2Sdan rc = sqlite3changeset_concat(
997cbf6d2d2Sdan sLeft.nData, sLeft.aData, sRight.nData, sRight.aData, &sOut.n, &sOut.p
998cbf6d2d2Sdan );
9995d607a6eSdan }
1000cbf6d2d2Sdan
1001cbf6d2d2Sdan if( rc!=SQLITE_OK ){
1002b9db9099Sdan rc = test_session_error(interp, rc, 0);
1003cbf6d2d2Sdan }else{
1004cbf6d2d2Sdan Tcl_SetObjResult(interp,Tcl_NewByteArrayObj((unsigned char*)sOut.p,sOut.n));
1005cbf6d2d2Sdan }
1006cbf6d2d2Sdan sqlite3_free(sOut.p);
1007cbf6d2d2Sdan return rc;
10085d607a6eSdan }
10095d607a6eSdan
10105d607a6eSdan /*
10114fccf43aSdan ** sqlite3session_foreach VARNAME CHANGESET SCRIPT
10124fccf43aSdan */
test_sqlite3session_foreach(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1013afe18262Smistachkin static int SQLITE_TCLAPI test_sqlite3session_foreach(
10144fccf43aSdan void * clientData,
10154fccf43aSdan Tcl_Interp *interp,
10164fccf43aSdan int objc,
10174fccf43aSdan Tcl_Obj *CONST objv[]
10184fccf43aSdan ){
10194757c658Sdan void *pChangeset;
10204757c658Sdan int nChangeset;
10214fccf43aSdan sqlite3_changeset_iter *pIter;
10224fccf43aSdan int rc;
10236734007dSdan Tcl_Obj *pVarname;
10246734007dSdan Tcl_Obj *pCS;
10256734007dSdan Tcl_Obj *pScript;
10266734007dSdan int isCheckNext = 0;
102746de0728Sdan int isInvert = 0;
10284fccf43aSdan
10294757c658Sdan TestStreamInput sStr;
10304757c658Sdan memset(&sStr, 0, sizeof(sStr));
10314757c658Sdan
103246de0728Sdan while( objc>1 ){
10336734007dSdan char *zOpt = Tcl_GetString(objv[1]);
103446de0728Sdan int nOpt = strlen(zOpt);
103546de0728Sdan if( zOpt[0]!='-' ) break;
103646de0728Sdan if( nOpt<=7 && 0==sqlite3_strnicmp(zOpt, "-invert", nOpt) ){
103746de0728Sdan isInvert = 1;
103846de0728Sdan }else
103946de0728Sdan if( nOpt<=5 && 0==sqlite3_strnicmp(zOpt, "-next", nOpt) ){
104046de0728Sdan isCheckNext = 1;
104146de0728Sdan }else{
104246de0728Sdan break;
10436734007dSdan }
104446de0728Sdan objv++;
104546de0728Sdan objc--;
104646de0728Sdan }
104746de0728Sdan if( objc!=4 ){
104846de0728Sdan Tcl_WrongNumArgs(
104946de0728Sdan interp, 1, objv, "?-next? ?-invert? VARNAME CHANGESET SCRIPT");
10504fccf43aSdan return TCL_ERROR;
10514fccf43aSdan }
10524fccf43aSdan
105346de0728Sdan pVarname = objv[1];
105446de0728Sdan pCS = objv[2];
105546de0728Sdan pScript = objv[3];
10566734007dSdan
10574757c658Sdan pChangeset = (void *)Tcl_GetByteArrayFromObj(pCS, &nChangeset);
10584757c658Sdan sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
105946de0728Sdan if( isInvert ){
106046de0728Sdan int f = SQLITE_CHANGESETSTART_INVERT;
106146de0728Sdan if( sStr.nStream==0 ){
106246de0728Sdan rc = sqlite3changeset_start_v2(&pIter, nChangeset, pChangeset, f);
106346de0728Sdan }else{
106446de0728Sdan void *pCtx = (void*)&sStr;
106546de0728Sdan sStr.aData = (unsigned char*)pChangeset;
106646de0728Sdan sStr.nData = nChangeset;
106746de0728Sdan rc = sqlite3changeset_start_v2_strm(&pIter, testStreamInput, pCtx, f);
106846de0728Sdan }
106946de0728Sdan }else{
10704757c658Sdan if( sStr.nStream==0 ){
10714757c658Sdan rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
10724757c658Sdan }else{
10734757c658Sdan sStr.aData = (unsigned char*)pChangeset;
10744757c658Sdan sStr.nData = nChangeset;
1075f1a08ad8Sdrh rc = sqlite3changeset_start_strm(&pIter, testStreamInput, (void*)&sStr);
10764757c658Sdan }
107746de0728Sdan }
10784fccf43aSdan if( rc!=SQLITE_OK ){
1079b9db9099Sdan return test_session_error(interp, rc, 0);
10804fccf43aSdan }
10814fccf43aSdan
10824fccf43aSdan while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
10834fccf43aSdan int nCol; /* Number of columns in table */
1084e5754eecSdan int nCol2; /* Number of columns in table */
10854fccf43aSdan int op; /* SQLITE_INSERT, UPDATE or DELETE */
10864fccf43aSdan const char *zTab; /* Name of table change applies to */
10874fccf43aSdan Tcl_Obj *pVar; /* Tcl value to set $VARNAME to */
10884fccf43aSdan Tcl_Obj *pOld; /* Vector of old.* values */
10894fccf43aSdan Tcl_Obj *pNew; /* Vector of new.* values */
1090b4480e94Sdan int bIndirect;
10914fccf43aSdan
1092244593c8Sdan char *zPK;
1093244593c8Sdan unsigned char *abPK;
1094244593c8Sdan int i;
1095244593c8Sdan
1096082c96dfSdan /* Test that _fk_conflicts() returns SQLITE_MISUSE if called on this
1097082c96dfSdan ** iterator. */
1098082c96dfSdan int nDummy;
1099082c96dfSdan if( SQLITE_MISUSE!=sqlite3changeset_fk_conflicts(pIter, &nDummy) ){
1100082c96dfSdan sqlite3changeset_finalize(pIter);
1101082c96dfSdan return TCL_ERROR;
1102082c96dfSdan }
1103082c96dfSdan
1104b4480e94Sdan sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
11054fccf43aSdan pVar = Tcl_NewObj();
11064fccf43aSdan Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
11074fccf43aSdan op==SQLITE_INSERT ? "INSERT" :
11084fccf43aSdan op==SQLITE_UPDATE ? "UPDATE" :
11094fccf43aSdan "DELETE", -1
11104fccf43aSdan ));
1111244593c8Sdan
11124fccf43aSdan Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
1113b4480e94Sdan Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
11144fccf43aSdan
1115244593c8Sdan zPK = ckalloc(nCol+1);
1116244593c8Sdan memset(zPK, 0, nCol+1);
1117e5754eecSdan sqlite3changeset_pk(pIter, &abPK, &nCol2);
1118e5754eecSdan assert( nCol==nCol2 );
1119244593c8Sdan for(i=0; i<nCol; i++){
1120244593c8Sdan zPK[i] = (abPK[i] ? 'X' : '.');
1121244593c8Sdan }
1122244593c8Sdan Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
1123244593c8Sdan ckfree(zPK);
1124244593c8Sdan
11254fccf43aSdan pOld = Tcl_NewObj();
11264fccf43aSdan if( op!=SQLITE_INSERT ){
11274fccf43aSdan for(i=0; i<nCol; i++){
11284fccf43aSdan sqlite3_value *pVal;
11294fccf43aSdan sqlite3changeset_old(pIter, i, &pVal);
11304fccf43aSdan test_append_value(pOld, pVal);
11314fccf43aSdan }
11324fccf43aSdan }
11334fccf43aSdan pNew = Tcl_NewObj();
11344fccf43aSdan if( op!=SQLITE_DELETE ){
11354fccf43aSdan for(i=0; i<nCol; i++){
11364fccf43aSdan sqlite3_value *pVal;
11374fccf43aSdan sqlite3changeset_new(pIter, i, &pVal);
11384fccf43aSdan test_append_value(pNew, pVal);
11394fccf43aSdan }
11404fccf43aSdan }
11414fccf43aSdan Tcl_ListObjAppendElement(0, pVar, pOld);
11424fccf43aSdan Tcl_ListObjAppendElement(0, pVar, pNew);
11434fccf43aSdan
11446734007dSdan Tcl_ObjSetVar2(interp, pVarname, 0, pVar, 0);
11456734007dSdan rc = Tcl_EvalObjEx(interp, pScript, 0);
11464fccf43aSdan if( rc!=TCL_OK && rc!=TCL_CONTINUE ){
11474fccf43aSdan sqlite3changeset_finalize(pIter);
11484fccf43aSdan return rc==TCL_BREAK ? TCL_OK : rc;
11494fccf43aSdan }
11504fccf43aSdan }
11516734007dSdan
11526734007dSdan if( isCheckNext ){
11536734007dSdan int rc2 = sqlite3changeset_next(pIter);
11544fccf43aSdan rc = sqlite3changeset_finalize(pIter);
11556734007dSdan assert( (rc2==SQLITE_DONE && rc==SQLITE_OK) || rc2==rc );
11566734007dSdan }else{
11576734007dSdan rc = sqlite3changeset_finalize(pIter);
11586734007dSdan }
11594fccf43aSdan if( rc!=SQLITE_OK ){
1160b9db9099Sdan return test_session_error(interp, rc, 0);
11614fccf43aSdan }
11624fccf43aSdan
11634fccf43aSdan return TCL_OK;
11644fccf43aSdan }
11654fccf43aSdan
1166f1b40e83Sdan /*
1167f1b40e83Sdan ** tclcmd: CMD configure REBASE-BLOB
1168f1b40e83Sdan ** tclcmd: CMD rebase CHANGESET
1169f1b40e83Sdan ** tclcmd: CMD delete
1170f1b40e83Sdan */
test_rebaser_cmd(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1171f1b40e83Sdan static int SQLITE_TCLAPI test_rebaser_cmd(
1172f1b40e83Sdan void * clientData,
1173f1b40e83Sdan Tcl_Interp *interp,
1174f1b40e83Sdan int objc,
1175f1b40e83Sdan Tcl_Obj *CONST objv[]
1176f1b40e83Sdan ){
117719f7bd3bSdan static struct RebaseSubcmd {
1178f1b40e83Sdan const char *zSub;
1179f1b40e83Sdan int nArg;
1180f1b40e83Sdan const char *zMsg;
1181f1b40e83Sdan int iSub;
1182f1b40e83Sdan } aSub[] = {
1183f1b40e83Sdan { "configure", 1, "REBASE-BLOB" }, /* 0 */
1184f1b40e83Sdan { "delete", 0, "" }, /* 1 */
1185f1b40e83Sdan { "rebase", 1, "CHANGESET" }, /* 2 */
1186f1b40e83Sdan { 0 }
1187f1b40e83Sdan };
1188f1b40e83Sdan
1189f1b40e83Sdan sqlite3_rebaser *p = (sqlite3_rebaser*)clientData;
1190f1b40e83Sdan int iSub;
1191f1b40e83Sdan int rc;
1192f1b40e83Sdan
1193f1b40e83Sdan if( objc<2 ){
1194f1b40e83Sdan Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
1195f1b40e83Sdan return TCL_ERROR;
1196f1b40e83Sdan }
1197f1b40e83Sdan rc = Tcl_GetIndexFromObjStruct(interp,
1198f1b40e83Sdan objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
1199f1b40e83Sdan );
1200f1b40e83Sdan if( rc!=TCL_OK ) return rc;
1201f1b40e83Sdan if( objc!=2+aSub[iSub].nArg ){
1202f1b40e83Sdan Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
1203f1b40e83Sdan return TCL_ERROR;
1204f1b40e83Sdan }
1205f1b40e83Sdan
1206f1b40e83Sdan assert( iSub==0 || iSub==1 || iSub==2 );
1207f1b40e83Sdan assert( rc==SQLITE_OK );
1208f1b40e83Sdan switch( iSub ){
1209f1b40e83Sdan case 0: { /* configure */
1210f1b40e83Sdan int nRebase = 0;
1211f1b40e83Sdan unsigned char *pRebase = Tcl_GetByteArrayFromObj(objv[2], &nRebase);
1212f1b40e83Sdan rc = sqlite3rebaser_configure(p, nRebase, pRebase);
1213f1b40e83Sdan break;
1214f1b40e83Sdan }
1215f1b40e83Sdan
1216f1b40e83Sdan case 1: /* delete */
1217f1b40e83Sdan Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
1218f1b40e83Sdan break;
1219f1b40e83Sdan
1220f1b40e83Sdan default: { /* rebase */
1221f1b40e83Sdan TestStreamInput sStr; /* Input stream */
1222f1b40e83Sdan TestSessionsBlob sOut; /* Output blob */
1223f1b40e83Sdan
1224f1b40e83Sdan memset(&sStr, 0, sizeof(sStr));
1225f1b40e83Sdan memset(&sOut, 0, sizeof(sOut));
1226f1b40e83Sdan sStr.aData = Tcl_GetByteArrayFromObj(objv[2], &sStr.nData);
1227f1b40e83Sdan sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
1228f1b40e83Sdan
1229f1b40e83Sdan if( sStr.nStream ){
1230f1b40e83Sdan rc = sqlite3rebaser_rebase_strm(p,
1231f1b40e83Sdan testStreamInput, (void*)&sStr,
1232f1b40e83Sdan testStreamOutput, (void*)&sOut
1233f1b40e83Sdan );
1234f1b40e83Sdan }else{
1235f1b40e83Sdan rc = sqlite3rebaser_rebase(p, sStr.nData, sStr.aData, &sOut.n, &sOut.p);
1236f1b40e83Sdan }
1237f1b40e83Sdan
1238f1b40e83Sdan if( rc==SQLITE_OK ){
1239f1b40e83Sdan Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(sOut.p, sOut.n));
1240f1b40e83Sdan }
1241f1b40e83Sdan sqlite3_free(sOut.p);
1242f1b40e83Sdan break;
1243f1b40e83Sdan }
1244f1b40e83Sdan }
1245f1b40e83Sdan
1246f1b40e83Sdan if( rc!=SQLITE_OK ){
1247f1b40e83Sdan return test_session_error(interp, rc, 0);
1248f1b40e83Sdan }
1249f1b40e83Sdan return TCL_OK;
1250f1b40e83Sdan }
1251f1b40e83Sdan
test_rebaser_del(void * clientData)1252f1b40e83Sdan static void SQLITE_TCLAPI test_rebaser_del(void *clientData){
1253f1b40e83Sdan sqlite3_rebaser *p = (sqlite3_rebaser*)clientData;
1254f1b40e83Sdan sqlite3rebaser_delete(p);
1255f1b40e83Sdan }
1256f1b40e83Sdan
1257f1b40e83Sdan /*
1258f1b40e83Sdan ** tclcmd: sqlite3rebaser_create NAME
1259f1b40e83Sdan */
test_sqlite3rebaser_create(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1260f1b40e83Sdan static int SQLITE_TCLAPI test_sqlite3rebaser_create(
1261f1b40e83Sdan void * clientData,
1262f1b40e83Sdan Tcl_Interp *interp,
1263f1b40e83Sdan int objc,
1264f1b40e83Sdan Tcl_Obj *CONST objv[]
1265f1b40e83Sdan ){
1266f1b40e83Sdan int rc;
1267f1b40e83Sdan sqlite3_rebaser *pNew = 0;
1268f1b40e83Sdan if( objc!=2 ){
1269f1b40e83Sdan Tcl_WrongNumArgs(interp, 1, objv, "NAME");
1270f1b40e83Sdan return SQLITE_ERROR;
1271f1b40e83Sdan }
1272f1b40e83Sdan
1273f1b40e83Sdan rc = sqlite3rebaser_create(&pNew);
1274f1b40e83Sdan if( rc!=SQLITE_OK ){
1275f1b40e83Sdan return test_session_error(interp, rc, 0);
1276f1b40e83Sdan }
1277f1b40e83Sdan
1278f1b40e83Sdan Tcl_CreateObjCommand(interp, Tcl_GetString(objv[1]), test_rebaser_cmd,
1279f1b40e83Sdan (ClientData)pNew, test_rebaser_del
1280f1b40e83Sdan );
1281f1b40e83Sdan Tcl_SetObjResult(interp, objv[1]);
1282f1b40e83Sdan return TCL_OK;
1283f1b40e83Sdan }
1284f1b40e83Sdan
12851f48e67dSdan /*
12861f48e67dSdan ** tclcmd: sqlite3rebaser_configure OP VALUE
12871f48e67dSdan */
test_sqlite3session_config(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])12881f48e67dSdan static int SQLITE_TCLAPI test_sqlite3session_config(
12891f48e67dSdan void * clientData,
12901f48e67dSdan Tcl_Interp *interp,
12911f48e67dSdan int objc,
12921f48e67dSdan Tcl_Obj *CONST objv[]
12931f48e67dSdan ){
129419f7bd3bSdan static struct ConfigOpt {
12951f48e67dSdan const char *zSub;
12961f48e67dSdan int op;
12971f48e67dSdan } aSub[] = {
12981f48e67dSdan { "strm_size", SQLITE_SESSION_CONFIG_STRMSIZE },
12991f48e67dSdan { "invalid", 0 },
13001f48e67dSdan { 0 }
13011f48e67dSdan };
13021f48e67dSdan int rc;
13031f48e67dSdan int iSub;
13041f48e67dSdan int iVal;
13051f48e67dSdan
13061f48e67dSdan if( objc!=3 ){
13071f48e67dSdan Tcl_WrongNumArgs(interp, 1, objv, "OP VALUE");
13081f48e67dSdan return SQLITE_ERROR;
13091f48e67dSdan }
13101f48e67dSdan rc = Tcl_GetIndexFromObjStruct(interp,
13111f48e67dSdan objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
13121f48e67dSdan );
13131f48e67dSdan if( rc!=TCL_OK ) return rc;
13141f48e67dSdan if( Tcl_GetIntFromObj(interp, objv[2], &iVal) ) return TCL_ERROR;
13151f48e67dSdan
13161f48e67dSdan rc = sqlite3session_config(aSub[iSub].op, (void*)&iVal);
13171f48e67dSdan if( rc!=SQLITE_OK ){
13181f48e67dSdan return test_session_error(interp, rc, 0);
13191f48e67dSdan }
13201f48e67dSdan Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal));
13211f48e67dSdan return TCL_OK;
13221f48e67dSdan }
13231f48e67dSdan
TestSession_Init(Tcl_Interp * interp)13244fccf43aSdan int TestSession_Init(Tcl_Interp *interp){
1325a87070a2Sdan struct Cmd {
1326a87070a2Sdan const char *zCmd;
1327a87070a2Sdan Tcl_ObjCmdProc *xProc;
1328a87070a2Sdan } aCmd[] = {
1329a87070a2Sdan { "sqlite3session", test_sqlite3session },
1330a87070a2Sdan { "sqlite3session_foreach", test_sqlite3session_foreach },
1331a87070a2Sdan { "sqlite3changeset_invert", test_sqlite3changeset_invert },
1332a87070a2Sdan { "sqlite3changeset_concat", test_sqlite3changeset_concat },
1333a87070a2Sdan { "sqlite3changeset_apply", test_sqlite3changeset_apply },
1334a38e6c57Sdan { "sqlite3changeset_apply_v2", test_sqlite3changeset_apply_v2 },
1335a87070a2Sdan { "sqlite3changeset_apply_replace_all",
1336a87070a2Sdan test_sqlite3changeset_apply_replace_all },
1337a87070a2Sdan { "sql_exec_changeset", test_sql_exec_changeset },
1338f1b40e83Sdan { "sqlite3rebaser_create", test_sqlite3rebaser_create },
13391f48e67dSdan { "sqlite3session_config", test_sqlite3session_config },
1340a87070a2Sdan };
1341a87070a2Sdan int i;
1342a87070a2Sdan
1343a87070a2Sdan for(i=0; i<sizeof(aCmd)/sizeof(struct Cmd); i++){
1344a87070a2Sdan struct Cmd *p = &aCmd[i];
1345a87070a2Sdan Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, 0, 0);
1346a87070a2Sdan }
1347a87070a2Sdan
13484fccf43aSdan return TCL_OK;
13494fccf43aSdan }
13504fccf43aSdan
13519b1c62d4Sdrh #endif /* SQLITE_TEST && SQLITE_SESSION && SQLITE_PREUPDATE_HOOK */
1352