xref: /sqlite-3.40.0/ext/session/test_session.c (revision 6d29a4fe)
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