test_session.c 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753
  1. #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_SESSION) \
  2. && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
  3. #include "sqlite3session.h"
  4. #include <assert.h>
  5. #include <string.h>
  6. #include "tclsqlite.h"
  7. #ifndef SQLITE_AMALGAMATION
  8. typedef unsigned char u8;
  9. #endif
  10. typedef struct TestSession TestSession;
  11. struct TestSession {
  12. sqlite3_session *pSession;
  13. Tcl_Interp *interp;
  14. Tcl_Obj *pFilterScript;
  15. };
  16. typedef struct TestStreamInput TestStreamInput;
  17. struct TestStreamInput {
  18. int nStream; /* Maximum chunk size */
  19. unsigned char *aData; /* Pointer to buffer containing data */
  20. int nData; /* Size of buffer aData in bytes */
  21. int iData; /* Bytes of data already read by sessions */
  22. };
  23. /*
  24. ** Extract an sqlite3* db handle from the object passed as the second
  25. ** argument. If successful, set *pDb to point to the db handle and return
  26. ** TCL_OK. Otherwise, return TCL_ERROR.
  27. */
  28. static int dbHandleFromObj(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){
  29. Tcl_CmdInfo info;
  30. if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){
  31. Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(pObj), 0);
  32. return TCL_ERROR;
  33. }
  34. *pDb = *(sqlite3 **)info.objClientData;
  35. return TCL_OK;
  36. }
  37. /*************************************************************************
  38. ** The following code is copied byte-for-byte from the sessions module
  39. ** documentation. It is used by some of the sessions modules tests to
  40. ** ensure that the example in the documentation does actually work.
  41. */
  42. /*
  43. ** Argument zSql points to a buffer containing an SQL script to execute
  44. ** against the database handle passed as the first argument. As well as
  45. ** executing the SQL script, this function collects a changeset recording
  46. ** all changes made to the "main" database file. Assuming no error occurs,
  47. ** output variables (*ppChangeset) and (*pnChangeset) are set to point
  48. ** to a buffer containing the changeset and the size of the changeset in
  49. ** bytes before returning SQLITE_OK. In this case it is the responsibility
  50. ** of the caller to eventually free the changeset blob by passing it to
  51. ** the sqlite3_free function.
  52. **
  53. ** Or, if an error does occur, return an SQLite error code. The final
  54. ** value of (*pChangeset) and (*pnChangeset) are undefined in this case.
  55. */
  56. int sql_exec_changeset(
  57. sqlite3 *db, /* Database handle */
  58. const char *zSql, /* SQL script to execute */
  59. int *pnChangeset, /* OUT: Size of changeset blob in bytes */
  60. void **ppChangeset /* OUT: Pointer to changeset blob */
  61. ){
  62. sqlite3_session *pSession = 0;
  63. int rc;
  64. int val = 1;
  65. /* Create a new session object */
  66. rc = sqlite3session_create(db, "main", &pSession);
  67. sqlite3session_object_config(pSession, SQLITE_SESSION_OBJCONFIG_ROWID, &val);
  68. /* Configure the session object to record changes to all tables */
  69. if( rc==SQLITE_OK ) rc = sqlite3session_attach(pSession, NULL);
  70. /* Execute the SQL script */
  71. if( rc==SQLITE_OK ) rc = sqlite3_exec(db, zSql, 0, 0, 0);
  72. /* Collect the changeset */
  73. if( rc==SQLITE_OK ){
  74. rc = sqlite3session_changeset(pSession, pnChangeset, ppChangeset);
  75. }
  76. /* Delete the session object */
  77. sqlite3session_delete(pSession);
  78. return rc;
  79. }
  80. /************************************************************************/
  81. #ifdef SQLITE_DEBUG
  82. static int sqlite3_test_changeset(int, void *, char **);
  83. static void assert_changeset_is_ok(int n, void *p){
  84. char *z = 0;
  85. (void)sqlite3_test_changeset(n, p, &z);
  86. assert( z==0 );
  87. }
  88. #else
  89. # define assert_changeset_is_ok(n,p)
  90. #endif
  91. /*
  92. ** Tclcmd: sql_exec_changeset DB SQL
  93. */
  94. static int SQLITE_TCLAPI test_sql_exec_changeset(
  95. void * clientData,
  96. Tcl_Interp *interp,
  97. int objc,
  98. Tcl_Obj *CONST objv[]
  99. ){
  100. const char *zSql;
  101. sqlite3 *db;
  102. void *pChangeset;
  103. int nChangeset;
  104. int rc;
  105. if( objc!=3 ){
  106. Tcl_WrongNumArgs(interp, 1, objv, "DB SQL");
  107. return TCL_ERROR;
  108. }
  109. if( dbHandleFromObj(interp, objv[1], &db) ) return TCL_ERROR;
  110. zSql = (const char*)Tcl_GetString(objv[2]);
  111. rc = sql_exec_changeset(db, zSql, &nChangeset, &pChangeset);
  112. if( rc!=SQLITE_OK ){
  113. Tcl_ResetResult(interp);
  114. Tcl_AppendResult(interp, "error in sql_exec_changeset()", 0);
  115. return TCL_ERROR;
  116. }
  117. assert_changeset_is_ok(nChangeset, pChangeset);
  118. Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pChangeset, nChangeset));
  119. sqlite3_free(pChangeset);
  120. return TCL_OK;
  121. }
  122. #define SESSION_STREAM_TCL_VAR "sqlite3session_streams"
  123. /*
  124. ** Attempt to find the global variable zVar within interpreter interp
  125. ** and extract an integer value from it. Return this value.
  126. **
  127. ** If the named variable cannot be found, or if it cannot be interpreted
  128. ** as a integer, return 0.
  129. */
  130. static int test_tcl_integer(Tcl_Interp *interp, const char *zVar){
  131. Tcl_Obj *pObj;
  132. int iVal = 0;
  133. Tcl_Obj *pName = Tcl_NewStringObj(zVar, -1);
  134. Tcl_IncrRefCount(pName);
  135. pObj = Tcl_ObjGetVar2(interp, pName, 0, TCL_GLOBAL_ONLY);
  136. Tcl_DecrRefCount(pName);
  137. if( pObj ) Tcl_GetIntFromObj(0, pObj, &iVal);
  138. return iVal;
  139. }
  140. static int test_session_error(Tcl_Interp *interp, int rc, char *zErr){
  141. extern const char *sqlite3ErrName(int);
  142. Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
  143. if( zErr ){
  144. Tcl_AppendResult(interp, " - ", zErr, 0);
  145. sqlite3_free(zErr);
  146. }
  147. return TCL_ERROR;
  148. }
  149. static int test_table_filter(void *pCtx, const char *zTbl){
  150. TestSession *p = (TestSession*)pCtx;
  151. Tcl_Obj *pEval;
  152. int rc;
  153. int bRes = 0;
  154. pEval = Tcl_DuplicateObj(p->pFilterScript);
  155. Tcl_IncrRefCount(pEval);
  156. rc = Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zTbl, -1));
  157. if( rc==TCL_OK ){
  158. rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
  159. }
  160. if( rc==TCL_OK ){
  161. rc = Tcl_GetBooleanFromObj(p->interp, Tcl_GetObjResult(p->interp), &bRes);
  162. }
  163. if( rc!=TCL_OK ){
  164. /* printf("error: %s\n", Tcl_GetStringResult(p->interp)); */
  165. Tcl_BackgroundError(p->interp);
  166. }
  167. Tcl_DecrRefCount(pEval);
  168. return bRes;
  169. }
  170. struct TestSessionsBlob {
  171. void *p;
  172. int n;
  173. };
  174. typedef struct TestSessionsBlob TestSessionsBlob;
  175. static int testStreamOutput(
  176. void *pCtx,
  177. const void *pData,
  178. int nData
  179. ){
  180. TestSessionsBlob *pBlob = (TestSessionsBlob*)pCtx;
  181. char *pNew;
  182. assert( nData>0 );
  183. pNew = (char*)sqlite3_realloc(pBlob->p, pBlob->n + nData);
  184. if( pNew==0 ){
  185. return SQLITE_NOMEM;
  186. }
  187. pBlob->p = (void*)pNew;
  188. memcpy(&pNew[pBlob->n], pData, nData);
  189. pBlob->n += nData;
  190. return SQLITE_OK;
  191. }
  192. /*
  193. ** Tclcmd: $session attach TABLE
  194. ** $session changeset
  195. ** $session delete
  196. ** $session enable BOOL
  197. ** $session indirect INTEGER
  198. ** $session patchset
  199. ** $session table_filter SCRIPT
  200. */
  201. static int SQLITE_TCLAPI test_session_cmd(
  202. void *clientData,
  203. Tcl_Interp *interp,
  204. int objc,
  205. Tcl_Obj *CONST objv[]
  206. ){
  207. TestSession *p = (TestSession*)clientData;
  208. sqlite3_session *pSession = p->pSession;
  209. static struct SessionSubcmd {
  210. const char *zSub;
  211. int nArg;
  212. const char *zMsg;
  213. int iSub;
  214. } aSub[] = {
  215. { "attach", 1, "TABLE", }, /* 0 */
  216. { "changeset", 0, "", }, /* 1 */
  217. { "delete", 0, "", }, /* 2 */
  218. { "enable", 1, "BOOL", }, /* 3 */
  219. { "indirect", 1, "BOOL", }, /* 4 */
  220. { "isempty", 0, "", }, /* 5 */
  221. { "table_filter", 1, "SCRIPT", }, /* 6 */
  222. { "patchset", 0, "", }, /* 7 */
  223. { "diff", 2, "FROMDB TBL", }, /* 8 */
  224. { "memory_used", 0, "", }, /* 9 */
  225. { "changeset_size", 0, "", }, /* 10 */
  226. { "object_config", 2, "OPTION INTEGER", }, /* 11 */
  227. { 0 }
  228. };
  229. int iSub;
  230. int rc;
  231. if( objc<2 ){
  232. Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
  233. return TCL_ERROR;
  234. }
  235. rc = Tcl_GetIndexFromObjStruct(interp,
  236. objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
  237. );
  238. if( rc!=TCL_OK ) return rc;
  239. if( objc!=2+aSub[iSub].nArg ){
  240. Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
  241. return TCL_ERROR;
  242. }
  243. switch( iSub ){
  244. case 0: { /* attach */
  245. char *zArg = Tcl_GetString(objv[2]);
  246. if( zArg[0]=='*' && zArg[1]=='\0' ) zArg = 0;
  247. rc = sqlite3session_attach(pSession, zArg);
  248. if( rc!=SQLITE_OK ){
  249. return test_session_error(interp, rc, 0);
  250. }
  251. break;
  252. }
  253. case 7: /* patchset */
  254. case 1: { /* changeset */
  255. TestSessionsBlob o = {0, 0};
  256. if( test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
  257. void *pCtx = (void*)&o;
  258. if( iSub==7 ){
  259. rc = sqlite3session_patchset_strm(pSession, testStreamOutput, pCtx);
  260. }else{
  261. rc = sqlite3session_changeset_strm(pSession, testStreamOutput, pCtx);
  262. }
  263. }else{
  264. if( iSub==7 ){
  265. rc = sqlite3session_patchset(pSession, &o.n, &o.p);
  266. }else{
  267. rc = sqlite3session_changeset(pSession, &o.n, &o.p);
  268. }
  269. }
  270. if( rc==SQLITE_OK ){
  271. assert_changeset_is_ok(o.n, o.p);
  272. Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(o.p, o.n));
  273. }
  274. sqlite3_free(o.p);
  275. if( rc!=SQLITE_OK ){
  276. return test_session_error(interp, rc, 0);
  277. }
  278. break;
  279. }
  280. case 2: /* delete */
  281. Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
  282. break;
  283. case 3: { /* enable */
  284. int val;
  285. if( Tcl_GetIntFromObj(interp, objv[2], &val) ) return TCL_ERROR;
  286. val = sqlite3session_enable(pSession, val);
  287. Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
  288. break;
  289. }
  290. case 4: { /* indirect */
  291. int val;
  292. if( Tcl_GetIntFromObj(interp, objv[2], &val) ) return TCL_ERROR;
  293. val = sqlite3session_indirect(pSession, val);
  294. Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
  295. break;
  296. }
  297. case 5: { /* isempty */
  298. int val;
  299. val = sqlite3session_isempty(pSession);
  300. Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
  301. break;
  302. }
  303. case 6: { /* table_filter */
  304. if( p->pFilterScript ) Tcl_DecrRefCount(p->pFilterScript);
  305. p->interp = interp;
  306. p->pFilterScript = Tcl_DuplicateObj(objv[2]);
  307. Tcl_IncrRefCount(p->pFilterScript);
  308. sqlite3session_table_filter(pSession, test_table_filter, clientData);
  309. break;
  310. }
  311. case 8: { /* diff */
  312. char *zErr = 0;
  313. rc = sqlite3session_diff(pSession,
  314. Tcl_GetString(objv[2]),
  315. Tcl_GetString(objv[3]),
  316. &zErr
  317. );
  318. assert( rc!=SQLITE_OK || zErr==0 );
  319. if( rc ){
  320. return test_session_error(interp, rc, zErr);
  321. }
  322. break;
  323. }
  324. case 9: { /* memory_used */
  325. sqlite3_int64 nMalloc = sqlite3session_memory_used(pSession);
  326. Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nMalloc));
  327. break;
  328. }
  329. case 10: {
  330. sqlite3_int64 nSize = sqlite3session_changeset_size(pSession);
  331. Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nSize));
  332. break;
  333. }
  334. case 11: { /* object_config */
  335. struct ObjConfOpt {
  336. const char *zName;
  337. int opt;
  338. } aOpt[] = {
  339. { "size", SQLITE_SESSION_OBJCONFIG_SIZE },
  340. { "rowid", SQLITE_SESSION_OBJCONFIG_ROWID },
  341. { 0, 0 }
  342. };
  343. int sz = (int)sizeof(aOpt[0]);
  344. int iArg;
  345. Tcl_Size iOpt;
  346. if( Tcl_GetIndexFromObjStruct(interp,objv[2],aOpt,sz,"option",0,&iOpt) ){
  347. return TCL_ERROR;
  348. }
  349. if( Tcl_GetIntFromObj(interp, objv[3], &iArg) ){
  350. return TCL_ERROR;
  351. }
  352. rc = sqlite3session_object_config(pSession, aOpt[iOpt].opt, &iArg);
  353. if( rc!=SQLITE_OK ){
  354. extern const char *sqlite3ErrName(int);
  355. Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
  356. }else{
  357. Tcl_SetObjResult(interp, Tcl_NewIntObj(iArg));
  358. }
  359. break;
  360. }
  361. }
  362. return TCL_OK;
  363. }
  364. static void SQLITE_TCLAPI test_session_del(void *clientData){
  365. TestSession *p = (TestSession*)clientData;
  366. if( p->pFilterScript ) Tcl_DecrRefCount(p->pFilterScript);
  367. sqlite3session_delete(p->pSession);
  368. ckfree((char*)p);
  369. }
  370. /*
  371. ** Tclcmd: sqlite3session CMD DB-HANDLE DB-NAME
  372. */
  373. static int SQLITE_TCLAPI test_sqlite3session(
  374. void * clientData,
  375. Tcl_Interp *interp,
  376. int objc,
  377. Tcl_Obj *CONST objv[]
  378. ){
  379. sqlite3 *db;
  380. Tcl_CmdInfo info;
  381. int rc; /* sqlite3session_create() return code */
  382. TestSession *p; /* New wrapper object */
  383. int iArg = -1;
  384. if( objc!=4 ){
  385. Tcl_WrongNumArgs(interp, 1, objv, "CMD DB-HANDLE DB-NAME");
  386. return TCL_ERROR;
  387. }
  388. if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[2]), &info) ){
  389. Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0);
  390. return TCL_ERROR;
  391. }
  392. db = *(sqlite3 **)info.objClientData;
  393. p = (TestSession*)ckalloc(sizeof(TestSession));
  394. memset(p, 0, sizeof(TestSession));
  395. rc = sqlite3session_create(db, Tcl_GetString(objv[3]), &p->pSession);
  396. if( rc!=SQLITE_OK ){
  397. ckfree((char*)p);
  398. return test_session_error(interp, rc, 0);
  399. }
  400. /* Query the SQLITE_SESSION_OBJCONFIG_SIZE option to ensure that it
  401. ** is clear by default. Then set it. */
  402. sqlite3session_object_config(p->pSession,SQLITE_SESSION_OBJCONFIG_SIZE,&iArg);
  403. assert( iArg==0 );
  404. iArg = 1;
  405. sqlite3session_object_config(p->pSession,SQLITE_SESSION_OBJCONFIG_SIZE,&iArg);
  406. Tcl_CreateObjCommand(
  407. interp, Tcl_GetString(objv[1]), test_session_cmd, (ClientData)p,
  408. test_session_del
  409. );
  410. Tcl_SetObjResult(interp, objv[1]);
  411. return TCL_OK;
  412. }
  413. static void test_append_value(Tcl_Obj *pList, sqlite3_value *pVal){
  414. if( pVal==0 ){
  415. Tcl_ListObjAppendElement(0, pList, Tcl_NewObj());
  416. Tcl_ListObjAppendElement(0, pList, Tcl_NewObj());
  417. }else{
  418. Tcl_Obj *pObj;
  419. switch( sqlite3_value_type(pVal) ){
  420. case SQLITE_NULL:
  421. Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("n", 1));
  422. pObj = Tcl_NewObj();
  423. break;
  424. case SQLITE_INTEGER:
  425. Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("i", 1));
  426. pObj = Tcl_NewWideIntObj(sqlite3_value_int64(pVal));
  427. break;
  428. case SQLITE_FLOAT:
  429. Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("f", 1));
  430. pObj = Tcl_NewDoubleObj(sqlite3_value_double(pVal));
  431. break;
  432. case SQLITE_TEXT: {
  433. const char *z = (char*)sqlite3_value_blob(pVal);
  434. int n = sqlite3_value_bytes(pVal);
  435. Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("t", 1));
  436. pObj = Tcl_NewStringObj(z, n);
  437. break;
  438. }
  439. default:
  440. assert( sqlite3_value_type(pVal)==SQLITE_BLOB );
  441. Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("b", 1));
  442. pObj = Tcl_NewByteArrayObj(
  443. sqlite3_value_blob(pVal),
  444. sqlite3_value_bytes(pVal)
  445. );
  446. break;
  447. }
  448. Tcl_ListObjAppendElement(0, pList, pObj);
  449. }
  450. }
  451. typedef struct TestConflictHandler TestConflictHandler;
  452. struct TestConflictHandler {
  453. Tcl_Interp *interp;
  454. Tcl_Obj *pConflictScript;
  455. Tcl_Obj *pFilterScript;
  456. };
  457. static int test_obj_eq_string(Tcl_Obj *p, const char *z){
  458. Tcl_Size n;
  459. Tcl_Size nObj;
  460. char *zObj;
  461. n = (Tcl_Size)strlen(z);
  462. zObj = Tcl_GetStringFromObj(p, &nObj);
  463. return (nObj==n && (n==0 || 0==memcmp(zObj, z, n)));
  464. }
  465. static int test_filter_handler(
  466. void *pCtx, /* Pointer to TestConflictHandler structure */
  467. const char *zTab /* Table name */
  468. ){
  469. TestConflictHandler *p = (TestConflictHandler *)pCtx;
  470. int res = 1;
  471. Tcl_Obj *pEval;
  472. Tcl_Interp *interp = p->interp;
  473. pEval = Tcl_DuplicateObj(p->pFilterScript);
  474. Tcl_IncrRefCount(pEval);
  475. if( TCL_OK!=Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zTab, -1))
  476. || TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL)
  477. || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &res)
  478. ){
  479. Tcl_BackgroundError(interp);
  480. }
  481. Tcl_DecrRefCount(pEval);
  482. return res;
  483. }
  484. static int test_conflict_handler(
  485. void *pCtx, /* Pointer to TestConflictHandler structure */
  486. int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */
  487. sqlite3_changeset_iter *pIter /* Handle describing change and conflict */
  488. ){
  489. TestConflictHandler *p = (TestConflictHandler *)pCtx;
  490. Tcl_Obj *pEval;
  491. Tcl_Interp *interp = p->interp;
  492. int ret = 0; /* Return value */
  493. int op; /* SQLITE_UPDATE, DELETE or INSERT */
  494. const char *zTab; /* Name of table conflict is on */
  495. int nCol; /* Number of columns in table zTab */
  496. pEval = Tcl_DuplicateObj(p->pConflictScript);
  497. Tcl_IncrRefCount(pEval);
  498. sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
  499. if( eConf==SQLITE_CHANGESET_FOREIGN_KEY ){
  500. int nFk;
  501. sqlite3changeset_fk_conflicts(pIter, &nFk);
  502. Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj("FOREIGN_KEY", -1));
  503. Tcl_ListObjAppendElement(0, pEval, Tcl_NewIntObj(nFk));
  504. }else{
  505. /* Append the operation type. */
  506. Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(
  507. op==SQLITE_INSERT ? "INSERT" :
  508. op==SQLITE_UPDATE ? "UPDATE" :
  509. "DELETE", -1
  510. ));
  511. /* Append the table name. */
  512. Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zTab, -1));
  513. /* Append the conflict type. */
  514. switch( eConf ){
  515. case SQLITE_CHANGESET_DATA:
  516. Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("DATA",-1));
  517. break;
  518. case SQLITE_CHANGESET_NOTFOUND:
  519. Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("NOTFOUND",-1));
  520. break;
  521. case SQLITE_CHANGESET_CONFLICT:
  522. Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("CONFLICT",-1));
  523. break;
  524. case SQLITE_CHANGESET_CONSTRAINT:
  525. Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("CONSTRAINT",-1));
  526. break;
  527. }
  528. /* If this is not an INSERT, append the old row */
  529. if( op!=SQLITE_INSERT ){
  530. int i;
  531. Tcl_Obj *pOld = Tcl_NewObj();
  532. for(i=0; i<nCol; i++){
  533. sqlite3_value *pVal;
  534. sqlite3changeset_old(pIter, i, &pVal);
  535. test_append_value(pOld, pVal);
  536. }
  537. Tcl_ListObjAppendElement(0, pEval, pOld);
  538. }
  539. /* If this is not a DELETE, append the new row */
  540. if( op!=SQLITE_DELETE ){
  541. int i;
  542. Tcl_Obj *pNew = Tcl_NewObj();
  543. for(i=0; i<nCol; i++){
  544. sqlite3_value *pVal;
  545. sqlite3changeset_new(pIter, i, &pVal);
  546. test_append_value(pNew, pVal);
  547. }
  548. Tcl_ListObjAppendElement(0, pEval, pNew);
  549. }
  550. /* If this is a CHANGESET_DATA or CHANGESET_CONFLICT conflict, append
  551. ** the conflicting row. */
  552. if( eConf==SQLITE_CHANGESET_DATA || eConf==SQLITE_CHANGESET_CONFLICT ){
  553. int i;
  554. Tcl_Obj *pConflict = Tcl_NewObj();
  555. for(i=0; i<nCol; i++){
  556. int rc;
  557. sqlite3_value *pVal;
  558. rc = sqlite3changeset_conflict(pIter, i, &pVal);
  559. assert( rc==SQLITE_OK );
  560. test_append_value(pConflict, pVal);
  561. }
  562. Tcl_ListObjAppendElement(0, pEval, pConflict);
  563. }
  564. /***********************************************************************
  565. ** This block is purely for testing some error conditions.
  566. */
  567. if( eConf==SQLITE_CHANGESET_CONSTRAINT
  568. || eConf==SQLITE_CHANGESET_NOTFOUND
  569. ){
  570. sqlite3_value *pVal;
  571. int rc = sqlite3changeset_conflict(pIter, 0, &pVal);
  572. assert( rc==SQLITE_MISUSE );
  573. }else{
  574. sqlite3_value *pVal;
  575. int rc = sqlite3changeset_conflict(pIter, -1, &pVal);
  576. assert( rc==SQLITE_RANGE );
  577. rc = sqlite3changeset_conflict(pIter, nCol, &pVal);
  578. assert( rc==SQLITE_RANGE );
  579. }
  580. if( op==SQLITE_DELETE ){
  581. sqlite3_value *pVal;
  582. int rc = sqlite3changeset_new(pIter, 0, &pVal);
  583. assert( rc==SQLITE_MISUSE );
  584. }else{
  585. sqlite3_value *pVal;
  586. int rc = sqlite3changeset_new(pIter, -1, &pVal);
  587. assert( rc==SQLITE_RANGE );
  588. rc = sqlite3changeset_new(pIter, nCol, &pVal);
  589. assert( rc==SQLITE_RANGE );
  590. }
  591. if( op==SQLITE_INSERT ){
  592. sqlite3_value *pVal;
  593. int rc = sqlite3changeset_old(pIter, 0, &pVal);
  594. assert( rc==SQLITE_MISUSE );
  595. }else{
  596. sqlite3_value *pVal;
  597. int rc = sqlite3changeset_old(pIter, -1, &pVal);
  598. assert( rc==SQLITE_RANGE );
  599. rc = sqlite3changeset_old(pIter, nCol, &pVal);
  600. assert( rc==SQLITE_RANGE );
  601. }
  602. if( eConf!=SQLITE_CHANGESET_FOREIGN_KEY ){
  603. /* eConf!=FOREIGN_KEY is always true at this point. The condition is
  604. ** just there to make it clearer what is being tested. */
  605. int nDummy;
  606. int rc = sqlite3changeset_fk_conflicts(pIter, &nDummy);
  607. assert( rc==SQLITE_MISUSE );
  608. }
  609. /* End of testing block
  610. ***********************************************************************/
  611. }
  612. if( TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL) ){
  613. Tcl_BackgroundError(interp);
  614. }else{
  615. Tcl_Obj *pRes = Tcl_GetObjResult(interp);
  616. if( test_obj_eq_string(pRes, "OMIT") || test_obj_eq_string(pRes, "") ){
  617. ret = SQLITE_CHANGESET_OMIT;
  618. }else if( test_obj_eq_string(pRes, "REPLACE") ){
  619. ret = SQLITE_CHANGESET_REPLACE;
  620. }else if( test_obj_eq_string(pRes, "ABORT") ){
  621. ret = SQLITE_CHANGESET_ABORT;
  622. }else{
  623. Tcl_GetIntFromObj(0, pRes, &ret);
  624. }
  625. }
  626. Tcl_DecrRefCount(pEval);
  627. return ret;
  628. }
  629. /*
  630. ** The conflict handler used by sqlite3changeset_apply_replace_all().
  631. ** This conflict handler calls sqlite3_value_text16() on all available
  632. ** sqlite3_value objects and then returns CHANGESET_REPLACE, or
  633. ** CHANGESET_OMIT if REPLACE is not applicable. This is used to test the
  634. ** effect of a malloc failure within an sqlite3_value_xxx() function
  635. ** invoked by a conflict-handler callback.
  636. */
  637. static int replace_handler(
  638. void *pCtx, /* Pointer to TestConflictHandler structure */
  639. int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */
  640. sqlite3_changeset_iter *pIter /* Handle describing change and conflict */
  641. ){
  642. int op; /* SQLITE_UPDATE, DELETE or INSERT */
  643. const char *zTab; /* Name of table conflict is on */
  644. int nCol; /* Number of columns in table zTab */
  645. int i;
  646. int x = 0;
  647. sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
  648. if( op!=SQLITE_INSERT ){
  649. for(i=0; i<nCol; i++){
  650. sqlite3_value *pVal;
  651. sqlite3changeset_old(pIter, i, &pVal);
  652. sqlite3_value_text16(pVal);
  653. x++;
  654. }
  655. }
  656. if( op!=SQLITE_DELETE ){
  657. for(i=0; i<nCol; i++){
  658. sqlite3_value *pVal;
  659. sqlite3changeset_new(pIter, i, &pVal);
  660. sqlite3_value_text16(pVal);
  661. x++;
  662. }
  663. }
  664. if( eConf==SQLITE_CHANGESET_DATA ){
  665. return SQLITE_CHANGESET_REPLACE;
  666. }
  667. return SQLITE_CHANGESET_OMIT;
  668. }
  669. static int testStreamInput(
  670. void *pCtx, /* Context pointer */
  671. void *pData, /* Buffer to populate */
  672. int *pnData /* IN/OUT: Bytes requested/supplied */
  673. ){
  674. TestStreamInput *p = (TestStreamInput*)pCtx;
  675. int nReq = *pnData; /* Bytes of data requested */
  676. int nRem = p->nData - p->iData; /* Bytes of data available */
  677. int nRet = p->nStream; /* Bytes actually returned */
  678. /* Allocate and free some space. There is no point to this, other than
  679. ** that it allows the regular OOM fault-injection tests to cause an error
  680. ** in this function. */
  681. void *pAlloc = sqlite3_malloc(10);
  682. if( pAlloc==0 ) return SQLITE_NOMEM;
  683. sqlite3_free(pAlloc);
  684. if( nRet>nReq ) nRet = nReq;
  685. if( nRet>nRem ) nRet = nRem;
  686. assert( nRet>=0 );
  687. if( nRet>0 ){
  688. memcpy(pData, &p->aData[p->iData], nRet);
  689. p->iData += nRet;
  690. }
  691. *pnData = nRet;
  692. return SQLITE_OK;
  693. }
  694. static int SQLITE_TCLAPI testSqlite3changesetApply(
  695. int bV2,
  696. void * clientData,
  697. Tcl_Interp *interp,
  698. int objc,
  699. Tcl_Obj *CONST objv[]
  700. ){
  701. sqlite3 *db; /* Database handle */
  702. Tcl_CmdInfo info; /* Database Tcl command (objv[1]) info */
  703. int rc; /* Return code from changeset_invert() */
  704. void *pChangeset; /* Buffer containing changeset */
  705. Tcl_Size nChangeset; /* Size of buffer aChangeset in bytes */
  706. TestConflictHandler ctx;
  707. TestStreamInput sStr;
  708. void *pRebase = 0;
  709. int nRebase = 0;
  710. int flags = 0; /* Flags for apply_v2() */
  711. memset(&sStr, 0, sizeof(sStr));
  712. sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
  713. /* Check for the -nosavepoint, -invert or -ignorenoop switches */
  714. if( bV2 ){
  715. while( objc>1 ){
  716. const char *z1 = Tcl_GetString(objv[1]);
  717. int n = (int)strlen(z1);
  718. if( n>3 && n<=12 && 0==sqlite3_strnicmp("-nosavepoint", z1, n) ){
  719. flags |= SQLITE_CHANGESETAPPLY_NOSAVEPOINT;
  720. }
  721. else if( n>3 && n<=9 && 0==sqlite3_strnicmp("-noaction", z1, n) ){
  722. flags |= SQLITE_CHANGESETAPPLY_FKNOACTION;
  723. }
  724. else if( n>2 && n<=7 && 0==sqlite3_strnicmp("-invert", z1, n) ){
  725. flags |= SQLITE_CHANGESETAPPLY_INVERT;
  726. }
  727. else if( n>2 && n<=11 && 0==sqlite3_strnicmp("-ignorenoop", z1, n) ){
  728. flags |= SQLITE_CHANGESETAPPLY_IGNORENOOP;
  729. }else{
  730. break;
  731. }
  732. objc--;
  733. objv++;
  734. }
  735. }
  736. if( objc!=4 && objc!=5 ){
  737. const char *zMsg;
  738. if( bV2 ){
  739. zMsg = "?-nosavepoint? ?-inverse? ?-ignorenoop? "
  740. "DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?";
  741. }else{
  742. zMsg = "DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?";
  743. }
  744. Tcl_WrongNumArgs(interp, 1, objv, zMsg);
  745. return TCL_ERROR;
  746. }
  747. if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &info) ){
  748. Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[1]), 0);
  749. return TCL_ERROR;
  750. }
  751. db = *(sqlite3 **)info.objClientData;
  752. pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset);
  753. ctx.pConflictScript = objv[3];
  754. ctx.pFilterScript = objc==5 ? objv[4] : 0;
  755. ctx.interp = interp;
  756. if( sStr.nStream==0 ){
  757. if( bV2==0 ){
  758. rc = sqlite3changeset_apply(db, (int)nChangeset, pChangeset,
  759. (objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx
  760. );
  761. }else{
  762. rc = sqlite3changeset_apply_v2(db, (int)nChangeset, pChangeset,
  763. (objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx,
  764. &pRebase, &nRebase, flags
  765. );
  766. }
  767. }else{
  768. sStr.aData = (unsigned char*)pChangeset;
  769. sStr.nData = (int)nChangeset;
  770. if( bV2==0 ){
  771. rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
  772. (objc==5) ? test_filter_handler : 0,
  773. test_conflict_handler, (void *)&ctx
  774. );
  775. }else{
  776. rc = sqlite3changeset_apply_v2_strm(db, testStreamInput, (void*)&sStr,
  777. (objc==5) ? test_filter_handler : 0,
  778. test_conflict_handler, (void *)&ctx,
  779. &pRebase, &nRebase, flags
  780. );
  781. }
  782. }
  783. if( rc!=SQLITE_OK ){
  784. return test_session_error(interp, rc, 0);
  785. }else{
  786. Tcl_ResetResult(interp);
  787. if( bV2 && pRebase ){
  788. Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pRebase, nRebase));
  789. }
  790. }
  791. sqlite3_free(pRebase);
  792. return TCL_OK;
  793. }
  794. /*
  795. ** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
  796. */
  797. static int SQLITE_TCLAPI test_sqlite3changeset_apply(
  798. void * clientData,
  799. Tcl_Interp *interp,
  800. int objc,
  801. Tcl_Obj *CONST objv[]
  802. ){
  803. return testSqlite3changesetApply(0, clientData, interp, objc, objv);
  804. }
  805. /*
  806. ** sqlite3changeset_apply_v2 DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
  807. */
  808. static int SQLITE_TCLAPI test_sqlite3changeset_apply_v2(
  809. void * clientData,
  810. Tcl_Interp *interp,
  811. int objc,
  812. Tcl_Obj *CONST objv[]
  813. ){
  814. return testSqlite3changesetApply(1, clientData, interp, objc, objv);
  815. }
  816. /*
  817. ** sqlite3changeset_apply_replace_all DB CHANGESET
  818. */
  819. static int SQLITE_TCLAPI test_sqlite3changeset_apply_replace_all(
  820. void * clientData,
  821. Tcl_Interp *interp,
  822. int objc,
  823. Tcl_Obj *CONST objv[]
  824. ){
  825. sqlite3 *db; /* Database handle */
  826. Tcl_CmdInfo info; /* Database Tcl command (objv[1]) info */
  827. int rc; /* Return code from changeset_invert() */
  828. void *pChangeset; /* Buffer containing changeset */
  829. Tcl_Size nChangeset; /* Size of buffer aChangeset in bytes */
  830. if( objc!=3 ){
  831. Tcl_WrongNumArgs(interp, 1, objv, "DB CHANGESET");
  832. return TCL_ERROR;
  833. }
  834. if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &info) ){
  835. Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0);
  836. return TCL_ERROR;
  837. }
  838. db = *(sqlite3 **)info.objClientData;
  839. pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset);
  840. rc = sqlite3changeset_apply(db, (int)nChangeset, pChangeset,
  841. 0, replace_handler,0);
  842. if( rc!=SQLITE_OK ){
  843. return test_session_error(interp, rc, 0);
  844. }
  845. Tcl_ResetResult(interp);
  846. return TCL_OK;
  847. }
  848. /*
  849. ** sqlite3changeset_invert CHANGESET
  850. */
  851. static int SQLITE_TCLAPI test_sqlite3changeset_invert(
  852. void * clientData,
  853. Tcl_Interp *interp,
  854. int objc,
  855. Tcl_Obj *CONST objv[]
  856. ){
  857. int rc; /* Return code from changeset_invert() */
  858. Tcl_Size nn;
  859. TestStreamInput sIn; /* Input stream */
  860. TestSessionsBlob sOut; /* Output blob */
  861. if( objc!=2 ){
  862. Tcl_WrongNumArgs(interp, 1, objv, "CHANGESET");
  863. return TCL_ERROR;
  864. }
  865. memset(&sIn, 0, sizeof(sIn));
  866. memset(&sOut, 0, sizeof(sOut));
  867. sIn.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
  868. sIn.aData = Tcl_GetByteArrayFromObj(objv[1], &nn);
  869. sIn.nData = (int)nn;
  870. if( sIn.nStream ){
  871. rc = sqlite3changeset_invert_strm(
  872. testStreamInput, (void*)&sIn, testStreamOutput, (void*)&sOut
  873. );
  874. }else{
  875. rc = sqlite3changeset_invert(sIn.nData, sIn.aData, &sOut.n, &sOut.p);
  876. }
  877. if( rc!=SQLITE_OK ){
  878. rc = test_session_error(interp, rc, 0);
  879. }else{
  880. assert_changeset_is_ok(sOut.n, sOut.p);
  881. Tcl_SetObjResult(interp,Tcl_NewByteArrayObj((unsigned char*)sOut.p,sOut.n));
  882. }
  883. sqlite3_free(sOut.p);
  884. return rc;
  885. }
  886. /*
  887. ** sqlite3changeset_concat LEFT RIGHT
  888. */
  889. static int SQLITE_TCLAPI test_sqlite3changeset_concat(
  890. void * clientData,
  891. Tcl_Interp *interp,
  892. int objc,
  893. Tcl_Obj *CONST objv[]
  894. ){
  895. int rc; /* Return code from changeset_invert() */
  896. Tcl_Size nn;
  897. TestStreamInput sLeft; /* Input stream */
  898. TestStreamInput sRight; /* Input stream */
  899. TestSessionsBlob sOut = {0,0}; /* Output blob */
  900. if( objc!=3 ){
  901. Tcl_WrongNumArgs(interp, 1, objv, "LEFT RIGHT");
  902. return TCL_ERROR;
  903. }
  904. memset(&sLeft, 0, sizeof(sLeft));
  905. memset(&sRight, 0, sizeof(sRight));
  906. sLeft.aData = Tcl_GetByteArrayFromObj(objv[1], &nn);
  907. sLeft.nData = (int)nn;
  908. sRight.aData = Tcl_GetByteArrayFromObj(objv[2], &nn);
  909. sRight.nData = (int)nn;
  910. sLeft.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
  911. sRight.nStream = sLeft.nStream;
  912. if( sLeft.nStream>0 ){
  913. rc = sqlite3changeset_concat_strm(
  914. testStreamInput, (void*)&sLeft,
  915. testStreamInput, (void*)&sRight,
  916. testStreamOutput, (void*)&sOut
  917. );
  918. }else{
  919. rc = sqlite3changeset_concat(
  920. sLeft.nData, sLeft.aData, sRight.nData, sRight.aData, &sOut.n, &sOut.p
  921. );
  922. }
  923. if( rc!=SQLITE_OK ){
  924. rc = test_session_error(interp, rc, 0);
  925. }else{
  926. assert_changeset_is_ok(sOut.n, sOut.p);
  927. Tcl_SetObjResult(interp,Tcl_NewByteArrayObj((unsigned char*)sOut.p,sOut.n));
  928. }
  929. sqlite3_free(sOut.p);
  930. return rc;
  931. }
  932. static Tcl_Obj *testIterData(sqlite3_changeset_iter *pIter){
  933. Tcl_Obj *pVar = 0;
  934. int nCol; /* Number of columns in table */
  935. int nCol2; /* Number of columns in table */
  936. int op; /* SQLITE_INSERT, UPDATE or DELETE */
  937. const char *zTab; /* Name of table change applies to */
  938. Tcl_Obj *pOld; /* Vector of old.* values */
  939. Tcl_Obj *pNew; /* Vector of new.* values */
  940. int bIndirect;
  941. char *zPK;
  942. unsigned char *abPK;
  943. int i;
  944. sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
  945. pVar = Tcl_NewObj();
  946. Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
  947. op==SQLITE_INSERT ? "INSERT" :
  948. op==SQLITE_UPDATE ? "UPDATE" :
  949. "DELETE", -1
  950. ));
  951. Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
  952. Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
  953. zPK = ckalloc(nCol+1);
  954. memset(zPK, 0, nCol+1);
  955. sqlite3changeset_pk(pIter, &abPK, &nCol2);
  956. assert( nCol==nCol2 );
  957. for(i=0; i<nCol; i++){
  958. zPK[i] = (abPK[i] ? 'X' : '.');
  959. }
  960. Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
  961. ckfree(zPK);
  962. pOld = Tcl_NewObj();
  963. if( op!=SQLITE_INSERT ){
  964. for(i=0; i<nCol; i++){
  965. sqlite3_value *pVal;
  966. sqlite3changeset_old(pIter, i, &pVal);
  967. test_append_value(pOld, pVal);
  968. }
  969. }
  970. pNew = Tcl_NewObj();
  971. if( op!=SQLITE_DELETE ){
  972. for(i=0; i<nCol; i++){
  973. sqlite3_value *pVal;
  974. sqlite3changeset_new(pIter, i, &pVal);
  975. test_append_value(pNew, pVal);
  976. }
  977. }
  978. Tcl_ListObjAppendElement(0, pVar, pOld);
  979. Tcl_ListObjAppendElement(0, pVar, pNew);
  980. return pVar;
  981. }
  982. /*
  983. ** sqlite3session_foreach VARNAME CHANGESET SCRIPT
  984. */
  985. static int SQLITE_TCLAPI test_sqlite3session_foreach(
  986. void * clientData,
  987. Tcl_Interp *interp,
  988. int objc,
  989. Tcl_Obj *CONST objv[]
  990. ){
  991. void *pChangeset;
  992. Tcl_Size nChangeset;
  993. sqlite3_changeset_iter *pIter;
  994. int rc;
  995. Tcl_Obj *pVarname;
  996. Tcl_Obj *pCS;
  997. Tcl_Obj *pScript;
  998. int isCheckNext = 0;
  999. int isInvert = 0;
  1000. TestStreamInput sStr;
  1001. memset(&sStr, 0, sizeof(sStr));
  1002. while( objc>1 ){
  1003. char *zOpt = Tcl_GetString(objv[1]);
  1004. int nOpt = (int)strlen(zOpt);
  1005. if( zOpt[0]!='-' ) break;
  1006. if( nOpt<=7 && 0==sqlite3_strnicmp(zOpt, "-invert", nOpt) ){
  1007. isInvert = 1;
  1008. }else
  1009. if( nOpt<=5 && 0==sqlite3_strnicmp(zOpt, "-next", nOpt) ){
  1010. isCheckNext = 1;
  1011. }else{
  1012. break;
  1013. }
  1014. objv++;
  1015. objc--;
  1016. }
  1017. if( objc!=4 ){
  1018. Tcl_WrongNumArgs(
  1019. interp, 1, objv, "?-next? ?-invert? VARNAME CHANGESET SCRIPT");
  1020. return TCL_ERROR;
  1021. }
  1022. pVarname = objv[1];
  1023. pCS = objv[2];
  1024. pScript = objv[3];
  1025. pChangeset = (void *)Tcl_GetByteArrayFromObj(pCS, &nChangeset);
  1026. sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
  1027. if( isInvert ){
  1028. int f = SQLITE_CHANGESETSTART_INVERT;
  1029. if( sStr.nStream==0 ){
  1030. rc = sqlite3changeset_start_v2(&pIter, (int)nChangeset, pChangeset, f);
  1031. }else{
  1032. void *pCtx = (void*)&sStr;
  1033. sStr.aData = (unsigned char*)pChangeset;
  1034. sStr.nData = (int)nChangeset;
  1035. rc = sqlite3changeset_start_v2_strm(&pIter, testStreamInput, pCtx, f);
  1036. }
  1037. }else{
  1038. if( sStr.nStream==0 ){
  1039. rc = sqlite3changeset_start(&pIter, (int)nChangeset, pChangeset);
  1040. }else{
  1041. sStr.aData = (unsigned char*)pChangeset;
  1042. sStr.nData = (int)nChangeset;
  1043. rc = sqlite3changeset_start_strm(&pIter, testStreamInput, (void*)&sStr);
  1044. }
  1045. }
  1046. if( rc!=SQLITE_OK ){
  1047. return test_session_error(interp, rc, 0);
  1048. }
  1049. while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
  1050. Tcl_Obj *pVar = 0; /* Tcl value to set $VARNAME to */
  1051. pVar = testIterData(pIter);
  1052. Tcl_ObjSetVar2(interp, pVarname, 0, pVar, 0);
  1053. rc = Tcl_EvalObjEx(interp, pScript, 0);
  1054. if( rc!=TCL_OK && rc!=TCL_CONTINUE ){
  1055. sqlite3changeset_finalize(pIter);
  1056. return rc==TCL_BREAK ? TCL_OK : rc;
  1057. }
  1058. }
  1059. if( isCheckNext ){
  1060. int rc2 = sqlite3changeset_next(pIter);
  1061. rc = sqlite3changeset_finalize(pIter);
  1062. assert( (rc2==SQLITE_DONE && rc==SQLITE_OK) || rc2==rc );
  1063. }else{
  1064. rc = sqlite3changeset_finalize(pIter);
  1065. }
  1066. if( rc!=SQLITE_OK ){
  1067. return test_session_error(interp, rc, 0);
  1068. }
  1069. return TCL_OK;
  1070. }
  1071. /*
  1072. ** tclcmd: CMD configure REBASE-BLOB
  1073. ** tclcmd: CMD rebase CHANGESET
  1074. ** tclcmd: CMD delete
  1075. */
  1076. static int SQLITE_TCLAPI test_rebaser_cmd(
  1077. void * clientData,
  1078. Tcl_Interp *interp,
  1079. int objc,
  1080. Tcl_Obj *CONST objv[]
  1081. ){
  1082. static struct RebaseSubcmd {
  1083. const char *zSub;
  1084. int nArg;
  1085. const char *zMsg;
  1086. int iSub;
  1087. } aSub[] = {
  1088. { "configure", 1, "REBASE-BLOB" }, /* 0 */
  1089. { "delete", 0, "" }, /* 1 */
  1090. { "rebase", 1, "CHANGESET" }, /* 2 */
  1091. { 0 }
  1092. };
  1093. sqlite3_rebaser *p = (sqlite3_rebaser*)clientData;
  1094. int iSub;
  1095. int rc;
  1096. if( objc<2 ){
  1097. Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
  1098. return TCL_ERROR;
  1099. }
  1100. rc = Tcl_GetIndexFromObjStruct(interp,
  1101. objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
  1102. );
  1103. if( rc!=TCL_OK ) return rc;
  1104. if( objc!=2+aSub[iSub].nArg ){
  1105. Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
  1106. return TCL_ERROR;
  1107. }
  1108. assert( iSub==0 || iSub==1 || iSub==2 );
  1109. assert( rc==SQLITE_OK );
  1110. switch( iSub ){
  1111. case 0: { /* configure */
  1112. Tcl_Size nRebase = 0;
  1113. unsigned char *pRebase = Tcl_GetByteArrayFromObj(objv[2], &nRebase);
  1114. rc = sqlite3rebaser_configure(p, (int)nRebase, pRebase);
  1115. break;
  1116. }
  1117. case 1: /* delete */
  1118. Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
  1119. break;
  1120. default: { /* rebase */
  1121. TestStreamInput sStr; /* Input stream */
  1122. TestSessionsBlob sOut; /* Output blob */
  1123. Tcl_Size nn;
  1124. memset(&sStr, 0, sizeof(sStr));
  1125. memset(&sOut, 0, sizeof(sOut));
  1126. sStr.aData = Tcl_GetByteArrayFromObj(objv[2], &nn);
  1127. sStr.nData = nn;
  1128. sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
  1129. if( sStr.nStream ){
  1130. rc = sqlite3rebaser_rebase_strm(p,
  1131. testStreamInput, (void*)&sStr,
  1132. testStreamOutput, (void*)&sOut
  1133. );
  1134. }else{
  1135. rc = sqlite3rebaser_rebase(p, sStr.nData, sStr.aData, &sOut.n, &sOut.p);
  1136. }
  1137. if( rc==SQLITE_OK ){
  1138. assert_changeset_is_ok(sOut.n, sOut.p);
  1139. Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(sOut.p, sOut.n));
  1140. }
  1141. sqlite3_free(sOut.p);
  1142. break;
  1143. }
  1144. }
  1145. if( rc!=SQLITE_OK ){
  1146. return test_session_error(interp, rc, 0);
  1147. }
  1148. return TCL_OK;
  1149. }
  1150. static void SQLITE_TCLAPI test_rebaser_del(void *clientData){
  1151. sqlite3_rebaser *p = (sqlite3_rebaser*)clientData;
  1152. sqlite3rebaser_delete(p);
  1153. }
  1154. /*
  1155. ** tclcmd: sqlite3rebaser_create NAME
  1156. */
  1157. static int SQLITE_TCLAPI test_sqlite3rebaser_create(
  1158. void * clientData,
  1159. Tcl_Interp *interp,
  1160. int objc,
  1161. Tcl_Obj *CONST objv[]
  1162. ){
  1163. int rc;
  1164. sqlite3_rebaser *pNew = 0;
  1165. if( objc!=2 ){
  1166. Tcl_WrongNumArgs(interp, 1, objv, "NAME");
  1167. return SQLITE_ERROR;
  1168. }
  1169. rc = sqlite3rebaser_create(&pNew);
  1170. if( rc!=SQLITE_OK ){
  1171. return test_session_error(interp, rc, 0);
  1172. }
  1173. Tcl_CreateObjCommand(interp, Tcl_GetString(objv[1]), test_rebaser_cmd,
  1174. (ClientData)pNew, test_rebaser_del
  1175. );
  1176. Tcl_SetObjResult(interp, objv[1]);
  1177. return TCL_OK;
  1178. }
  1179. /*
  1180. ** Run some sanity checks on the changeset in nChangeset byte buffer
  1181. ** pChangeset. If any fail, return a non-zero value and, optionally,
  1182. ** set output variable (*pzErr) to point to a buffer containing an
  1183. ** English language error message describing the problem. In this
  1184. ** case it is the responsibility of the caller to free the buffer
  1185. ** using sqlite3_free().
  1186. **
  1187. ** Or, if the changeset appears to be well-formed, this function
  1188. ** returns SQLITE_OK and sets (*pzErr) to NULL.
  1189. */
  1190. static int sqlite3_test_changeset(
  1191. int nChangeset,
  1192. void *pChangeset,
  1193. char **pzErr
  1194. ){
  1195. sqlite3_changeset_iter *pIter = 0;
  1196. char *zErr = 0;
  1197. int rc = SQLITE_OK;
  1198. int bPatch = (nChangeset>0 && ((char*)pChangeset)[0]=='P');
  1199. rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
  1200. if( rc==SQLITE_OK ){
  1201. int rc2;
  1202. while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter) ){
  1203. unsigned char *aPk = 0;
  1204. int nCol = 0;
  1205. int op = 0;
  1206. const char *zTab = 0;
  1207. sqlite3changeset_pk(pIter, &aPk, &nCol);
  1208. sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
  1209. if( op==SQLITE_UPDATE ){
  1210. int iCol;
  1211. for(iCol=0; iCol<nCol; iCol++){
  1212. sqlite3_value *pNew = 0;
  1213. sqlite3_value *pOld = 0;
  1214. sqlite3changeset_new(pIter, iCol, &pNew);
  1215. sqlite3changeset_old(pIter, iCol, &pOld);
  1216. if( aPk[iCol] ){
  1217. if( pOld==0 ) rc = SQLITE_ERROR;
  1218. }else if( bPatch ){
  1219. if( pOld ) rc = SQLITE_ERROR;
  1220. }else{
  1221. if( (pOld==0)!=(pNew==0) ) rc = SQLITE_ERROR;
  1222. }
  1223. if( rc!=SQLITE_OK ){
  1224. zErr = sqlite3_mprintf(
  1225. "unexpected SQLITE_UPDATE (bPatch=%d pk=%d pOld=%d pNew=%d)",
  1226. bPatch, (int)aPk[iCol], pOld!=0, pNew!=0
  1227. );
  1228. break;
  1229. }
  1230. }
  1231. }
  1232. }
  1233. rc2 = sqlite3changeset_finalize(pIter);
  1234. if( rc==SQLITE_OK ){
  1235. rc = rc2;
  1236. }
  1237. }
  1238. *pzErr = zErr;
  1239. return rc;
  1240. }
  1241. /*
  1242. ** test_changeset CHANGESET
  1243. */
  1244. static int SQLITE_TCLAPI test_changeset(
  1245. void * clientData,
  1246. Tcl_Interp *interp,
  1247. int objc,
  1248. Tcl_Obj *CONST objv[]
  1249. ){
  1250. void *pChangeset = 0; /* Buffer containing changeset */
  1251. Tcl_Size nChangeset = 0; /* Size of buffer aChangeset in bytes */
  1252. int rc = SQLITE_OK;
  1253. char *z = 0;
  1254. if( objc!=2 ){
  1255. Tcl_WrongNumArgs(interp, 1, objv, "CHANGESET");
  1256. return TCL_ERROR;
  1257. }
  1258. pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[1], &nChangeset);
  1259. Tcl_ResetResult(interp);
  1260. rc = sqlite3_test_changeset((int)nChangeset, pChangeset, &z);
  1261. if( rc!=SQLITE_OK ){
  1262. char *zErr = sqlite3_mprintf("(%d) - \"%s\"", rc, z);
  1263. Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
  1264. sqlite3_free(zErr);
  1265. }
  1266. sqlite3_free(z);
  1267. return rc ? TCL_ERROR : TCL_OK;
  1268. }
  1269. /*
  1270. ** tclcmd: sqlite3rebaser_configure OP VALUE
  1271. */
  1272. static int SQLITE_TCLAPI test_sqlite3session_config(
  1273. void * clientData,
  1274. Tcl_Interp *interp,
  1275. int objc,
  1276. Tcl_Obj *CONST objv[]
  1277. ){
  1278. static struct ConfigOpt {
  1279. const char *zSub;
  1280. int op;
  1281. } aSub[] = {
  1282. { "strm_size", SQLITE_SESSION_CONFIG_STRMSIZE },
  1283. { "invalid", 0 },
  1284. { 0 }
  1285. };
  1286. int rc;
  1287. int iSub;
  1288. int iVal;
  1289. if( objc!=3 ){
  1290. Tcl_WrongNumArgs(interp, 1, objv, "OP VALUE");
  1291. return SQLITE_ERROR;
  1292. }
  1293. rc = Tcl_GetIndexFromObjStruct(interp,
  1294. objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
  1295. );
  1296. if( rc!=TCL_OK ) return rc;
  1297. if( Tcl_GetIntFromObj(interp, objv[2], &iVal) ) return TCL_ERROR;
  1298. rc = sqlite3session_config(aSub[iSub].op, (void*)&iVal);
  1299. if( rc!=SQLITE_OK ){
  1300. return test_session_error(interp, rc, 0);
  1301. }
  1302. Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal));
  1303. return TCL_OK;
  1304. }
  1305. typedef struct TestChangegroup TestChangegroup;
  1306. struct TestChangegroup {
  1307. sqlite3_changegroup *pGrp;
  1308. };
  1309. typedef struct TestChangeIter TestChangeIter;
  1310. struct TestChangeIter {
  1311. sqlite3_changeset_iter *pIter;
  1312. };
  1313. /*
  1314. ** Destructor for Tcl changegroup command object.
  1315. */
  1316. static void test_changegroup_del(void *clientData){
  1317. TestChangegroup *pGrp = (TestChangegroup*)clientData;
  1318. sqlite3changegroup_delete(pGrp->pGrp);
  1319. ckfree(pGrp);
  1320. }
  1321. /*
  1322. ** Tclcmd: $changegroup schema DB DBNAME
  1323. ** Tclcmd: $changegroup add CHANGESET
  1324. ** Tclcmd: $changegroup output
  1325. ** Tclcmd: $changegroup delete
  1326. */
  1327. static int SQLITE_TCLAPI test_changegroup_cmd(
  1328. void * clientData,
  1329. Tcl_Interp *interp,
  1330. int objc,
  1331. Tcl_Obj *CONST objv[]
  1332. ){
  1333. TestChangegroup *p = (TestChangegroup*)clientData;
  1334. static struct ChangegroupCmd {
  1335. const char *zSub;
  1336. int nArg;
  1337. const char *zMsg;
  1338. int iSub;
  1339. } aSub[] = {
  1340. { "schema", 2, "DB DBNAME", }, /* 0 */
  1341. { "add", 1, "CHANGESET", }, /* 1 */
  1342. { "output", 0, "", }, /* 2 */
  1343. { "delete", 0, "", }, /* 3 */
  1344. { "add_change", 1, "ITERATOR", }, /* 4 */
  1345. { 0 }
  1346. };
  1347. int rc = TCL_OK;
  1348. int iSub = 0;
  1349. if( objc<2 ){
  1350. Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
  1351. return TCL_ERROR;
  1352. }
  1353. rc = Tcl_GetIndexFromObjStruct(interp,
  1354. objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
  1355. );
  1356. if( rc!=TCL_OK ) return rc;
  1357. if( objc!=2+aSub[iSub].nArg ){
  1358. Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
  1359. return TCL_ERROR;
  1360. }
  1361. switch( iSub ){
  1362. case 0: { /* schema */
  1363. sqlite3 *db = 0;
  1364. const char *zDb = Tcl_GetString(objv[3]);
  1365. if( dbHandleFromObj(interp, objv[2], &db) ){
  1366. return TCL_ERROR;
  1367. }
  1368. rc = sqlite3changegroup_schema(p->pGrp, db, zDb);
  1369. if( rc!=SQLITE_OK ) rc = test_session_error(interp, rc, 0);
  1370. break;
  1371. };
  1372. case 1: { /* add */
  1373. Tcl_Size nByte = 0;
  1374. const u8 *aByte = Tcl_GetByteArrayFromObj(objv[2], &nByte);
  1375. rc = sqlite3changegroup_add(p->pGrp, (int)nByte, (void*)aByte);
  1376. if( rc!=SQLITE_OK ) rc = test_session_error(interp, rc, 0);
  1377. break;
  1378. };
  1379. case 2: { /* output */
  1380. int nByte = 0;
  1381. u8 *aByte = 0;
  1382. rc = sqlite3changegroup_output(p->pGrp, &nByte, (void**)&aByte);
  1383. if( rc!=SQLITE_OK ){
  1384. rc = test_session_error(interp, rc, 0);
  1385. }else{
  1386. Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(aByte, nByte));
  1387. }
  1388. sqlite3_free(aByte);
  1389. break;
  1390. };
  1391. case 4: { /* add_change */
  1392. Tcl_CmdInfo cmdInfo; /* Database Tcl command (objv[2]) info */
  1393. TestChangeIter *pIter = 0;
  1394. const char *zIter = Tcl_GetString(objv[2]);
  1395. if( 0==Tcl_GetCommandInfo(interp, zIter, &cmdInfo) ){
  1396. Tcl_AppendResult(interp, "no such iter: ", Tcl_GetString(objv[2]), 0);
  1397. return TCL_ERROR;
  1398. }
  1399. pIter = (struct TestChangeIter*)cmdInfo.objClientData;
  1400. rc = sqlite3changegroup_add_change(p->pGrp, pIter->pIter);
  1401. if( rc!=SQLITE_OK ){
  1402. rc = test_session_error(interp, rc, 0);
  1403. }
  1404. break;
  1405. };
  1406. default: { /* delete */
  1407. assert( iSub==3 );
  1408. Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
  1409. break;
  1410. }
  1411. }
  1412. return rc;
  1413. }
  1414. /*
  1415. ** Tclcmd: sqlite3changegroup CMD
  1416. */
  1417. static int SQLITE_TCLAPI test_sqlite3changegroup(
  1418. void * clientData,
  1419. Tcl_Interp *interp,
  1420. int objc,
  1421. Tcl_Obj *CONST objv[]
  1422. ){
  1423. int rc; /* sqlite3changegroup_new() return code */
  1424. TestChangegroup *p; /* New wrapper object */
  1425. if( objc!=2 ){
  1426. Tcl_WrongNumArgs(interp, 1, objv, "CMD");
  1427. return TCL_ERROR;
  1428. }
  1429. p = (TestChangegroup*)ckalloc(sizeof(TestChangegroup));
  1430. memset(p, 0, sizeof(TestChangegroup));
  1431. rc = sqlite3changegroup_new(&p->pGrp);
  1432. if( rc!=SQLITE_OK ){
  1433. ckfree((char*)p);
  1434. return test_session_error(interp, rc, 0);
  1435. }
  1436. Tcl_CreateObjCommand(
  1437. interp, Tcl_GetString(objv[1]), test_changegroup_cmd, (ClientData)p,
  1438. test_changegroup_del
  1439. );
  1440. Tcl_SetObjResult(interp, objv[1]);
  1441. return TCL_OK;
  1442. }
  1443. extern const char *sqlite3ErrName(int);
  1444. /*
  1445. ** Destructor for Tcl iterator command object.
  1446. */
  1447. static void test_iter_del(void *clientData){
  1448. TestChangeIter *p = (TestChangeIter*)clientData;
  1449. sqlite3changeset_finalize(p->pIter);
  1450. ckfree(p);
  1451. }
  1452. static int SQLITE_TCLAPI test_iter_cmd(
  1453. void * clientData,
  1454. Tcl_Interp *interp,
  1455. int objc,
  1456. Tcl_Obj *CONST objv[]
  1457. ){
  1458. static const char *aSub[] = {
  1459. "next", /* 0 */
  1460. "data", /* 1 */
  1461. "finalize", /* 2 */
  1462. 0
  1463. };
  1464. int iSub = 0;
  1465. TestChangeIter *p = (TestChangeIter*)clientData;
  1466. int rc = SQLITE_OK;
  1467. if( objc<2 ){
  1468. Tcl_WrongNumArgs(interp, 1, objv, "CMD");
  1469. return TCL_ERROR;
  1470. }
  1471. if( Tcl_GetIndexFromObj(interp, objv[1], aSub, "sub-command", 0, &iSub) ){
  1472. return TCL_ERROR;
  1473. }
  1474. switch( iSub ){
  1475. case 0:
  1476. rc = sqlite3changeset_next(p->pIter);
  1477. Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
  1478. break;
  1479. case 1:
  1480. Tcl_SetObjResult(interp, testIterData(p->pIter));
  1481. break;
  1482. case 2:
  1483. rc = sqlite3changeset_finalize(p->pIter);
  1484. p->pIter = 0;
  1485. Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
  1486. Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
  1487. break;
  1488. default:
  1489. assert( 0 );
  1490. break;
  1491. }
  1492. return TCL_OK;
  1493. }
  1494. /*
  1495. ** Tclcmd: sqlite3changeset_start ?-invert? CHANGESET
  1496. */
  1497. static int SQLITE_TCLAPI test_sqlite3changeset_start(
  1498. void * clientData,
  1499. Tcl_Interp *interp,
  1500. int objc,
  1501. Tcl_Obj *CONST objv[]
  1502. ){
  1503. int isInvert = 0;
  1504. void *pChangeset = 0; /* Buffer containing changeset */
  1505. Tcl_Size nChangeset = 0; /* Size of buffer aChangeset in bytes */
  1506. TestChangeIter *pNew = 0;
  1507. sqlite3_changeset_iter *pIter = 0;
  1508. int flags = 0;
  1509. int rc = SQLITE_OK;
  1510. static int iCmd = 1;
  1511. char zCmd[64];
  1512. if( objc==3 ){
  1513. Tcl_Size n = 0;
  1514. const char *z = Tcl_GetStringFromObj(objv[1], &n);
  1515. isInvert = (n>=2 && sqlite3_strnicmp(z, "-invert", (int)n)==0);
  1516. }
  1517. if( objc!=2 && (objc!=3 || !isInvert) ){
  1518. Tcl_WrongNumArgs(interp, 1, objv, "?-invert? CHANGESET");
  1519. return TCL_ERROR;
  1520. }
  1521. flags = isInvert ? SQLITE_CHANGESETSTART_INVERT : 0;
  1522. pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[objc-1], &nChangeset);
  1523. rc = sqlite3changeset_start_v2(&pIter, (int)nChangeset, pChangeset, flags);
  1524. if( rc!=SQLITE_OK ){
  1525. char *zErr = sqlite3_mprintf(
  1526. "error in sqlite3changeset_start_v2() - %d", rc
  1527. );
  1528. Tcl_AppendResult(interp, zErr, (char*)0);
  1529. return TCL_ERROR;
  1530. }
  1531. pNew = (TestChangeIter*)ckalloc(sizeof(TestChangeIter));
  1532. pNew->pIter = pIter;
  1533. sprintf(zCmd, "csiter%d", iCmd++);
  1534. Tcl_CreateObjCommand(interp, zCmd, test_iter_cmd, (void*)pNew, test_iter_del);
  1535. Tcl_SetObjResult(interp, Tcl_NewStringObj(zCmd, -1));
  1536. return TCL_OK;
  1537. }
  1538. int TestSession_Init(Tcl_Interp *interp){
  1539. struct Cmd {
  1540. const char *zCmd;
  1541. Tcl_ObjCmdProc *xProc;
  1542. } aCmd[] = {
  1543. { "sqlite3session", test_sqlite3session },
  1544. { "sqlite3changegroup", test_sqlite3changegroup },
  1545. { "sqlite3changeset_start", test_sqlite3changeset_start },
  1546. { "sqlite3session_foreach", test_sqlite3session_foreach },
  1547. { "sqlite3changeset_invert", test_sqlite3changeset_invert },
  1548. { "sqlite3changeset_concat", test_sqlite3changeset_concat },
  1549. { "sqlite3changeset_apply", test_sqlite3changeset_apply },
  1550. { "sqlite3changeset_apply_v2", test_sqlite3changeset_apply_v2 },
  1551. { "sqlite3changeset_apply_replace_all",
  1552. test_sqlite3changeset_apply_replace_all },
  1553. { "sql_exec_changeset", test_sql_exec_changeset },
  1554. { "sqlite3rebaser_create", test_sqlite3rebaser_create },
  1555. { "sqlite3session_config", test_sqlite3session_config },
  1556. { "test_changeset", test_changeset },
  1557. };
  1558. int i;
  1559. for(i=0; i<sizeof(aCmd)/sizeof(struct Cmd); i++){
  1560. struct Cmd *p = &aCmd[i];
  1561. Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, 0, 0);
  1562. }
  1563. return TCL_OK;
  1564. }
  1565. #endif /* SQLITE_TEST && SQLITE_SESSION && SQLITE_PREUPDATE_HOOK */