1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753 |
- #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_SESSION) \
- && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
- #include "sqlite3session.h"
- #include <assert.h>
- #include <string.h>
- #include "tclsqlite.h"
- #ifndef SQLITE_AMALGAMATION
- typedef unsigned char u8;
- #endif
- typedef struct TestSession TestSession;
- struct TestSession {
- sqlite3_session *pSession;
- Tcl_Interp *interp;
- Tcl_Obj *pFilterScript;
- };
- typedef struct TestStreamInput TestStreamInput;
- struct TestStreamInput {
- int nStream; /* Maximum chunk size */
- unsigned char *aData; /* Pointer to buffer containing data */
- int nData; /* Size of buffer aData in bytes */
- int iData; /* Bytes of data already read by sessions */
- };
- /*
- ** Extract an sqlite3* db handle from the object passed as the second
- ** argument. If successful, set *pDb to point to the db handle and return
- ** TCL_OK. Otherwise, return TCL_ERROR.
- */
- static int dbHandleFromObj(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){
- Tcl_CmdInfo info;
- if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){
- Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(pObj), 0);
- return TCL_ERROR;
- }
- *pDb = *(sqlite3 **)info.objClientData;
- return TCL_OK;
- }
- /*************************************************************************
- ** The following code is copied byte-for-byte from the sessions module
- ** documentation. It is used by some of the sessions modules tests to
- ** ensure that the example in the documentation does actually work.
- */
- /*
- ** Argument zSql points to a buffer containing an SQL script to execute
- ** against the database handle passed as the first argument. As well as
- ** executing the SQL script, this function collects a changeset recording
- ** all changes made to the "main" database file. Assuming no error occurs,
- ** output variables (*ppChangeset) and (*pnChangeset) are set to point
- ** to a buffer containing the changeset and the size of the changeset in
- ** bytes before returning SQLITE_OK. In this case it is the responsibility
- ** of the caller to eventually free the changeset blob by passing it to
- ** the sqlite3_free function.
- **
- ** Or, if an error does occur, return an SQLite error code. The final
- ** value of (*pChangeset) and (*pnChangeset) are undefined in this case.
- */
- int sql_exec_changeset(
- sqlite3 *db, /* Database handle */
- const char *zSql, /* SQL script to execute */
- int *pnChangeset, /* OUT: Size of changeset blob in bytes */
- void **ppChangeset /* OUT: Pointer to changeset blob */
- ){
- sqlite3_session *pSession = 0;
- int rc;
- int val = 1;
- /* Create a new session object */
- rc = sqlite3session_create(db, "main", &pSession);
- sqlite3session_object_config(pSession, SQLITE_SESSION_OBJCONFIG_ROWID, &val);
- /* Configure the session object to record changes to all tables */
- if( rc==SQLITE_OK ) rc = sqlite3session_attach(pSession, NULL);
- /* Execute the SQL script */
- if( rc==SQLITE_OK ) rc = sqlite3_exec(db, zSql, 0, 0, 0);
- /* Collect the changeset */
- if( rc==SQLITE_OK ){
- rc = sqlite3session_changeset(pSession, pnChangeset, ppChangeset);
- }
- /* Delete the session object */
- sqlite3session_delete(pSession);
- return rc;
- }
- /************************************************************************/
- #ifdef SQLITE_DEBUG
- static int sqlite3_test_changeset(int, void *, char **);
- static void assert_changeset_is_ok(int n, void *p){
- char *z = 0;
- (void)sqlite3_test_changeset(n, p, &z);
- assert( z==0 );
- }
- #else
- # define assert_changeset_is_ok(n,p)
- #endif
- /*
- ** Tclcmd: sql_exec_changeset DB SQL
- */
- static int SQLITE_TCLAPI test_sql_exec_changeset(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- const char *zSql;
- sqlite3 *db;
- void *pChangeset;
- int nChangeset;
- int rc;
- if( objc!=3 ){
- Tcl_WrongNumArgs(interp, 1, objv, "DB SQL");
- return TCL_ERROR;
- }
- if( dbHandleFromObj(interp, objv[1], &db) ) return TCL_ERROR;
- zSql = (const char*)Tcl_GetString(objv[2]);
- rc = sql_exec_changeset(db, zSql, &nChangeset, &pChangeset);
- if( rc!=SQLITE_OK ){
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "error in sql_exec_changeset()", 0);
- return TCL_ERROR;
- }
- assert_changeset_is_ok(nChangeset, pChangeset);
- Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pChangeset, nChangeset));
- sqlite3_free(pChangeset);
- return TCL_OK;
- }
- #define SESSION_STREAM_TCL_VAR "sqlite3session_streams"
- /*
- ** Attempt to find the global variable zVar within interpreter interp
- ** and extract an integer value from it. Return this value.
- **
- ** If the named variable cannot be found, or if it cannot be interpreted
- ** as a integer, return 0.
- */
- static int test_tcl_integer(Tcl_Interp *interp, const char *zVar){
- Tcl_Obj *pObj;
- int iVal = 0;
- Tcl_Obj *pName = Tcl_NewStringObj(zVar, -1);
- Tcl_IncrRefCount(pName);
- pObj = Tcl_ObjGetVar2(interp, pName, 0, TCL_GLOBAL_ONLY);
- Tcl_DecrRefCount(pName);
- if( pObj ) Tcl_GetIntFromObj(0, pObj, &iVal);
- return iVal;
- }
- static int test_session_error(Tcl_Interp *interp, int rc, char *zErr){
- extern const char *sqlite3ErrName(int);
- Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
- if( zErr ){
- Tcl_AppendResult(interp, " - ", zErr, 0);
- sqlite3_free(zErr);
- }
- return TCL_ERROR;
- }
- static int test_table_filter(void *pCtx, const char *zTbl){
- TestSession *p = (TestSession*)pCtx;
- Tcl_Obj *pEval;
- int rc;
- int bRes = 0;
- pEval = Tcl_DuplicateObj(p->pFilterScript);
- Tcl_IncrRefCount(pEval);
- rc = Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zTbl, -1));
- if( rc==TCL_OK ){
- rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
- }
- if( rc==TCL_OK ){
- rc = Tcl_GetBooleanFromObj(p->interp, Tcl_GetObjResult(p->interp), &bRes);
- }
- if( rc!=TCL_OK ){
- /* printf("error: %s\n", Tcl_GetStringResult(p->interp)); */
- Tcl_BackgroundError(p->interp);
- }
- Tcl_DecrRefCount(pEval);
- return bRes;
- }
- struct TestSessionsBlob {
- void *p;
- int n;
- };
- typedef struct TestSessionsBlob TestSessionsBlob;
- static int testStreamOutput(
- void *pCtx,
- const void *pData,
- int nData
- ){
- TestSessionsBlob *pBlob = (TestSessionsBlob*)pCtx;
- char *pNew;
- assert( nData>0 );
- pNew = (char*)sqlite3_realloc(pBlob->p, pBlob->n + nData);
- if( pNew==0 ){
- return SQLITE_NOMEM;
- }
- pBlob->p = (void*)pNew;
- memcpy(&pNew[pBlob->n], pData, nData);
- pBlob->n += nData;
- return SQLITE_OK;
- }
- /*
- ** Tclcmd: $session attach TABLE
- ** $session changeset
- ** $session delete
- ** $session enable BOOL
- ** $session indirect INTEGER
- ** $session patchset
- ** $session table_filter SCRIPT
- */
- static int SQLITE_TCLAPI test_session_cmd(
- void *clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- TestSession *p = (TestSession*)clientData;
- sqlite3_session *pSession = p->pSession;
- static struct SessionSubcmd {
- const char *zSub;
- int nArg;
- const char *zMsg;
- int iSub;
- } aSub[] = {
- { "attach", 1, "TABLE", }, /* 0 */
- { "changeset", 0, "", }, /* 1 */
- { "delete", 0, "", }, /* 2 */
- { "enable", 1, "BOOL", }, /* 3 */
- { "indirect", 1, "BOOL", }, /* 4 */
- { "isempty", 0, "", }, /* 5 */
- { "table_filter", 1, "SCRIPT", }, /* 6 */
- { "patchset", 0, "", }, /* 7 */
- { "diff", 2, "FROMDB TBL", }, /* 8 */
- { "memory_used", 0, "", }, /* 9 */
- { "changeset_size", 0, "", }, /* 10 */
- { "object_config", 2, "OPTION INTEGER", }, /* 11 */
- { 0 }
- };
- int iSub;
- int rc;
- if( objc<2 ){
- Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
- return TCL_ERROR;
- }
- rc = Tcl_GetIndexFromObjStruct(interp,
- objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
- );
- if( rc!=TCL_OK ) return rc;
- if( objc!=2+aSub[iSub].nArg ){
- Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
- return TCL_ERROR;
- }
- switch( iSub ){
- case 0: { /* attach */
- char *zArg = Tcl_GetString(objv[2]);
- if( zArg[0]=='*' && zArg[1]=='\0' ) zArg = 0;
- rc = sqlite3session_attach(pSession, zArg);
- if( rc!=SQLITE_OK ){
- return test_session_error(interp, rc, 0);
- }
- break;
- }
- case 7: /* patchset */
- case 1: { /* changeset */
- TestSessionsBlob o = {0, 0};
- if( test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
- void *pCtx = (void*)&o;
- if( iSub==7 ){
- rc = sqlite3session_patchset_strm(pSession, testStreamOutput, pCtx);
- }else{
- rc = sqlite3session_changeset_strm(pSession, testStreamOutput, pCtx);
- }
- }else{
- if( iSub==7 ){
- rc = sqlite3session_patchset(pSession, &o.n, &o.p);
- }else{
- rc = sqlite3session_changeset(pSession, &o.n, &o.p);
- }
- }
- if( rc==SQLITE_OK ){
- assert_changeset_is_ok(o.n, o.p);
- Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(o.p, o.n));
- }
- sqlite3_free(o.p);
- if( rc!=SQLITE_OK ){
- return test_session_error(interp, rc, 0);
- }
- break;
- }
- case 2: /* delete */
- Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
- break;
- case 3: { /* enable */
- int val;
- if( Tcl_GetIntFromObj(interp, objv[2], &val) ) return TCL_ERROR;
- val = sqlite3session_enable(pSession, val);
- Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
- break;
- }
- case 4: { /* indirect */
- int val;
- if( Tcl_GetIntFromObj(interp, objv[2], &val) ) return TCL_ERROR;
- val = sqlite3session_indirect(pSession, val);
- Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
- break;
- }
- case 5: { /* isempty */
- int val;
- val = sqlite3session_isempty(pSession);
- Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
- break;
- }
-
- case 6: { /* table_filter */
- if( p->pFilterScript ) Tcl_DecrRefCount(p->pFilterScript);
- p->interp = interp;
- p->pFilterScript = Tcl_DuplicateObj(objv[2]);
- Tcl_IncrRefCount(p->pFilterScript);
- sqlite3session_table_filter(pSession, test_table_filter, clientData);
- break;
- }
- case 8: { /* diff */
- char *zErr = 0;
- rc = sqlite3session_diff(pSession,
- Tcl_GetString(objv[2]),
- Tcl_GetString(objv[3]),
- &zErr
- );
- assert( rc!=SQLITE_OK || zErr==0 );
- if( rc ){
- return test_session_error(interp, rc, zErr);
- }
- break;
- }
- case 9: { /* memory_used */
- sqlite3_int64 nMalloc = sqlite3session_memory_used(pSession);
- Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nMalloc));
- break;
- }
- case 10: {
- sqlite3_int64 nSize = sqlite3session_changeset_size(pSession);
- Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nSize));
- break;
- }
- case 11: { /* object_config */
- struct ObjConfOpt {
- const char *zName;
- int opt;
- } aOpt[] = {
- { "size", SQLITE_SESSION_OBJCONFIG_SIZE },
- { "rowid", SQLITE_SESSION_OBJCONFIG_ROWID },
- { 0, 0 }
- };
- int sz = (int)sizeof(aOpt[0]);
- int iArg;
- Tcl_Size iOpt;
- if( Tcl_GetIndexFromObjStruct(interp,objv[2],aOpt,sz,"option",0,&iOpt) ){
- return TCL_ERROR;
- }
- if( Tcl_GetIntFromObj(interp, objv[3], &iArg) ){
- return TCL_ERROR;
- }
- rc = sqlite3session_object_config(pSession, aOpt[iOpt].opt, &iArg);
- if( rc!=SQLITE_OK ){
- extern const char *sqlite3ErrName(int);
- Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
- }else{
- Tcl_SetObjResult(interp, Tcl_NewIntObj(iArg));
- }
- break;
- }
- }
- return TCL_OK;
- }
- static void SQLITE_TCLAPI test_session_del(void *clientData){
- TestSession *p = (TestSession*)clientData;
- if( p->pFilterScript ) Tcl_DecrRefCount(p->pFilterScript);
- sqlite3session_delete(p->pSession);
- ckfree((char*)p);
- }
- /*
- ** Tclcmd: sqlite3session CMD DB-HANDLE DB-NAME
- */
- static int SQLITE_TCLAPI test_sqlite3session(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- sqlite3 *db;
- Tcl_CmdInfo info;
- int rc; /* sqlite3session_create() return code */
- TestSession *p; /* New wrapper object */
- int iArg = -1;
- if( objc!=4 ){
- Tcl_WrongNumArgs(interp, 1, objv, "CMD DB-HANDLE DB-NAME");
- return TCL_ERROR;
- }
- if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[2]), &info) ){
- Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0);
- return TCL_ERROR;
- }
- db = *(sqlite3 **)info.objClientData;
- p = (TestSession*)ckalloc(sizeof(TestSession));
- memset(p, 0, sizeof(TestSession));
- rc = sqlite3session_create(db, Tcl_GetString(objv[3]), &p->pSession);
- if( rc!=SQLITE_OK ){
- ckfree((char*)p);
- return test_session_error(interp, rc, 0);
- }
- /* Query the SQLITE_SESSION_OBJCONFIG_SIZE option to ensure that it
- ** is clear by default. Then set it. */
- sqlite3session_object_config(p->pSession,SQLITE_SESSION_OBJCONFIG_SIZE,&iArg);
- assert( iArg==0 );
- iArg = 1;
- sqlite3session_object_config(p->pSession,SQLITE_SESSION_OBJCONFIG_SIZE,&iArg);
- Tcl_CreateObjCommand(
- interp, Tcl_GetString(objv[1]), test_session_cmd, (ClientData)p,
- test_session_del
- );
- Tcl_SetObjResult(interp, objv[1]);
- return TCL_OK;
- }
- static void test_append_value(Tcl_Obj *pList, sqlite3_value *pVal){
- if( pVal==0 ){
- Tcl_ListObjAppendElement(0, pList, Tcl_NewObj());
- Tcl_ListObjAppendElement(0, pList, Tcl_NewObj());
- }else{
- Tcl_Obj *pObj;
- switch( sqlite3_value_type(pVal) ){
- case SQLITE_NULL:
- Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("n", 1));
- pObj = Tcl_NewObj();
- break;
- case SQLITE_INTEGER:
- Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("i", 1));
- pObj = Tcl_NewWideIntObj(sqlite3_value_int64(pVal));
- break;
- case SQLITE_FLOAT:
- Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("f", 1));
- pObj = Tcl_NewDoubleObj(sqlite3_value_double(pVal));
- break;
- case SQLITE_TEXT: {
- const char *z = (char*)sqlite3_value_blob(pVal);
- int n = sqlite3_value_bytes(pVal);
- Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("t", 1));
- pObj = Tcl_NewStringObj(z, n);
- break;
- }
- default:
- assert( sqlite3_value_type(pVal)==SQLITE_BLOB );
- Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("b", 1));
- pObj = Tcl_NewByteArrayObj(
- sqlite3_value_blob(pVal),
- sqlite3_value_bytes(pVal)
- );
- break;
- }
- Tcl_ListObjAppendElement(0, pList, pObj);
- }
- }
- typedef struct TestConflictHandler TestConflictHandler;
- struct TestConflictHandler {
- Tcl_Interp *interp;
- Tcl_Obj *pConflictScript;
- Tcl_Obj *pFilterScript;
- };
- static int test_obj_eq_string(Tcl_Obj *p, const char *z){
- Tcl_Size n;
- Tcl_Size nObj;
- char *zObj;
- n = (Tcl_Size)strlen(z);
- zObj = Tcl_GetStringFromObj(p, &nObj);
- return (nObj==n && (n==0 || 0==memcmp(zObj, z, n)));
- }
- static int test_filter_handler(
- void *pCtx, /* Pointer to TestConflictHandler structure */
- const char *zTab /* Table name */
- ){
- TestConflictHandler *p = (TestConflictHandler *)pCtx;
- int res = 1;
- Tcl_Obj *pEval;
- Tcl_Interp *interp = p->interp;
- pEval = Tcl_DuplicateObj(p->pFilterScript);
- Tcl_IncrRefCount(pEval);
- if( TCL_OK!=Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zTab, -1))
- || TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL)
- || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &res)
- ){
- Tcl_BackgroundError(interp);
- }
- Tcl_DecrRefCount(pEval);
- return res;
- }
- static int test_conflict_handler(
- void *pCtx, /* Pointer to TestConflictHandler structure */
- int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */
- sqlite3_changeset_iter *pIter /* Handle describing change and conflict */
- ){
- TestConflictHandler *p = (TestConflictHandler *)pCtx;
- Tcl_Obj *pEval;
- Tcl_Interp *interp = p->interp;
- int ret = 0; /* Return value */
- int op; /* SQLITE_UPDATE, DELETE or INSERT */
- const char *zTab; /* Name of table conflict is on */
- int nCol; /* Number of columns in table zTab */
- pEval = Tcl_DuplicateObj(p->pConflictScript);
- Tcl_IncrRefCount(pEval);
- sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
- if( eConf==SQLITE_CHANGESET_FOREIGN_KEY ){
- int nFk;
- sqlite3changeset_fk_conflicts(pIter, &nFk);
- Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj("FOREIGN_KEY", -1));
- Tcl_ListObjAppendElement(0, pEval, Tcl_NewIntObj(nFk));
- }else{
- /* Append the operation type. */
- Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(
- op==SQLITE_INSERT ? "INSERT" :
- op==SQLITE_UPDATE ? "UPDATE" :
- "DELETE", -1
- ));
-
- /* Append the table name. */
- Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zTab, -1));
-
- /* Append the conflict type. */
- switch( eConf ){
- case SQLITE_CHANGESET_DATA:
- Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("DATA",-1));
- break;
- case SQLITE_CHANGESET_NOTFOUND:
- Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("NOTFOUND",-1));
- break;
- case SQLITE_CHANGESET_CONFLICT:
- Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("CONFLICT",-1));
- break;
- case SQLITE_CHANGESET_CONSTRAINT:
- Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("CONSTRAINT",-1));
- break;
- }
-
- /* If this is not an INSERT, append the old row */
- if( op!=SQLITE_INSERT ){
- int i;
- Tcl_Obj *pOld = Tcl_NewObj();
- for(i=0; i<nCol; i++){
- sqlite3_value *pVal;
- sqlite3changeset_old(pIter, i, &pVal);
- test_append_value(pOld, pVal);
- }
- Tcl_ListObjAppendElement(0, pEval, pOld);
- }
- /* If this is not a DELETE, append the new row */
- if( op!=SQLITE_DELETE ){
- int i;
- Tcl_Obj *pNew = Tcl_NewObj();
- for(i=0; i<nCol; i++){
- sqlite3_value *pVal;
- sqlite3changeset_new(pIter, i, &pVal);
- test_append_value(pNew, pVal);
- }
- Tcl_ListObjAppendElement(0, pEval, pNew);
- }
- /* If this is a CHANGESET_DATA or CHANGESET_CONFLICT conflict, append
- ** the conflicting row. */
- if( eConf==SQLITE_CHANGESET_DATA || eConf==SQLITE_CHANGESET_CONFLICT ){
- int i;
- Tcl_Obj *pConflict = Tcl_NewObj();
- for(i=0; i<nCol; i++){
- int rc;
- sqlite3_value *pVal;
- rc = sqlite3changeset_conflict(pIter, i, &pVal);
- assert( rc==SQLITE_OK );
- test_append_value(pConflict, pVal);
- }
- Tcl_ListObjAppendElement(0, pEval, pConflict);
- }
- /***********************************************************************
- ** This block is purely for testing some error conditions.
- */
- if( eConf==SQLITE_CHANGESET_CONSTRAINT
- || eConf==SQLITE_CHANGESET_NOTFOUND
- ){
- sqlite3_value *pVal;
- int rc = sqlite3changeset_conflict(pIter, 0, &pVal);
- assert( rc==SQLITE_MISUSE );
- }else{
- sqlite3_value *pVal;
- int rc = sqlite3changeset_conflict(pIter, -1, &pVal);
- assert( rc==SQLITE_RANGE );
- rc = sqlite3changeset_conflict(pIter, nCol, &pVal);
- assert( rc==SQLITE_RANGE );
- }
- if( op==SQLITE_DELETE ){
- sqlite3_value *pVal;
- int rc = sqlite3changeset_new(pIter, 0, &pVal);
- assert( rc==SQLITE_MISUSE );
- }else{
- sqlite3_value *pVal;
- int rc = sqlite3changeset_new(pIter, -1, &pVal);
- assert( rc==SQLITE_RANGE );
- rc = sqlite3changeset_new(pIter, nCol, &pVal);
- assert( rc==SQLITE_RANGE );
- }
- if( op==SQLITE_INSERT ){
- sqlite3_value *pVal;
- int rc = sqlite3changeset_old(pIter, 0, &pVal);
- assert( rc==SQLITE_MISUSE );
- }else{
- sqlite3_value *pVal;
- int rc = sqlite3changeset_old(pIter, -1, &pVal);
- assert( rc==SQLITE_RANGE );
- rc = sqlite3changeset_old(pIter, nCol, &pVal);
- assert( rc==SQLITE_RANGE );
- }
- if( eConf!=SQLITE_CHANGESET_FOREIGN_KEY ){
- /* eConf!=FOREIGN_KEY is always true at this point. The condition is
- ** just there to make it clearer what is being tested. */
- int nDummy;
- int rc = sqlite3changeset_fk_conflicts(pIter, &nDummy);
- assert( rc==SQLITE_MISUSE );
- }
- /* End of testing block
- ***********************************************************************/
- }
- if( TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL) ){
- Tcl_BackgroundError(interp);
- }else{
- Tcl_Obj *pRes = Tcl_GetObjResult(interp);
- if( test_obj_eq_string(pRes, "OMIT") || test_obj_eq_string(pRes, "") ){
- ret = SQLITE_CHANGESET_OMIT;
- }else if( test_obj_eq_string(pRes, "REPLACE") ){
- ret = SQLITE_CHANGESET_REPLACE;
- }else if( test_obj_eq_string(pRes, "ABORT") ){
- ret = SQLITE_CHANGESET_ABORT;
- }else{
- Tcl_GetIntFromObj(0, pRes, &ret);
- }
- }
- Tcl_DecrRefCount(pEval);
- return ret;
- }
- /*
- ** The conflict handler used by sqlite3changeset_apply_replace_all().
- ** This conflict handler calls sqlite3_value_text16() on all available
- ** sqlite3_value objects and then returns CHANGESET_REPLACE, or
- ** CHANGESET_OMIT if REPLACE is not applicable. This is used to test the
- ** effect of a malloc failure within an sqlite3_value_xxx() function
- ** invoked by a conflict-handler callback.
- */
- static int replace_handler(
- void *pCtx, /* Pointer to TestConflictHandler structure */
- int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */
- sqlite3_changeset_iter *pIter /* Handle describing change and conflict */
- ){
- int op; /* SQLITE_UPDATE, DELETE or INSERT */
- const char *zTab; /* Name of table conflict is on */
- int nCol; /* Number of columns in table zTab */
- int i;
- int x = 0;
- sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
- if( op!=SQLITE_INSERT ){
- for(i=0; i<nCol; i++){
- sqlite3_value *pVal;
- sqlite3changeset_old(pIter, i, &pVal);
- sqlite3_value_text16(pVal);
- x++;
- }
- }
- if( op!=SQLITE_DELETE ){
- for(i=0; i<nCol; i++){
- sqlite3_value *pVal;
- sqlite3changeset_new(pIter, i, &pVal);
- sqlite3_value_text16(pVal);
- x++;
- }
- }
- if( eConf==SQLITE_CHANGESET_DATA ){
- return SQLITE_CHANGESET_REPLACE;
- }
- return SQLITE_CHANGESET_OMIT;
- }
- static int testStreamInput(
- void *pCtx, /* Context pointer */
- void *pData, /* Buffer to populate */
- int *pnData /* IN/OUT: Bytes requested/supplied */
- ){
- TestStreamInput *p = (TestStreamInput*)pCtx;
- int nReq = *pnData; /* Bytes of data requested */
- int nRem = p->nData - p->iData; /* Bytes of data available */
- int nRet = p->nStream; /* Bytes actually returned */
- /* Allocate and free some space. There is no point to this, other than
- ** that it allows the regular OOM fault-injection tests to cause an error
- ** in this function. */
- void *pAlloc = sqlite3_malloc(10);
- if( pAlloc==0 ) return SQLITE_NOMEM;
- sqlite3_free(pAlloc);
- if( nRet>nReq ) nRet = nReq;
- if( nRet>nRem ) nRet = nRem;
- assert( nRet>=0 );
- if( nRet>0 ){
- memcpy(pData, &p->aData[p->iData], nRet);
- p->iData += nRet;
- }
- *pnData = nRet;
- return SQLITE_OK;
- }
- static int SQLITE_TCLAPI testSqlite3changesetApply(
- int bV2,
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- sqlite3 *db; /* Database handle */
- Tcl_CmdInfo info; /* Database Tcl command (objv[1]) info */
- int rc; /* Return code from changeset_invert() */
- void *pChangeset; /* Buffer containing changeset */
- Tcl_Size nChangeset; /* Size of buffer aChangeset in bytes */
- TestConflictHandler ctx;
- TestStreamInput sStr;
- void *pRebase = 0;
- int nRebase = 0;
- int flags = 0; /* Flags for apply_v2() */
- memset(&sStr, 0, sizeof(sStr));
- sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
- /* Check for the -nosavepoint, -invert or -ignorenoop switches */
- if( bV2 ){
- while( objc>1 ){
- const char *z1 = Tcl_GetString(objv[1]);
- int n = (int)strlen(z1);
- if( n>3 && n<=12 && 0==sqlite3_strnicmp("-nosavepoint", z1, n) ){
- flags |= SQLITE_CHANGESETAPPLY_NOSAVEPOINT;
- }
- else if( n>3 && n<=9 && 0==sqlite3_strnicmp("-noaction", z1, n) ){
- flags |= SQLITE_CHANGESETAPPLY_FKNOACTION;
- }
- else if( n>2 && n<=7 && 0==sqlite3_strnicmp("-invert", z1, n) ){
- flags |= SQLITE_CHANGESETAPPLY_INVERT;
- }
- else if( n>2 && n<=11 && 0==sqlite3_strnicmp("-ignorenoop", z1, n) ){
- flags |= SQLITE_CHANGESETAPPLY_IGNORENOOP;
- }else{
- break;
- }
- objc--;
- objv++;
- }
- }
- if( objc!=4 && objc!=5 ){
- const char *zMsg;
- if( bV2 ){
- zMsg = "?-nosavepoint? ?-inverse? ?-ignorenoop? "
- "DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?";
- }else{
- zMsg = "DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?";
- }
- Tcl_WrongNumArgs(interp, 1, objv, zMsg);
- return TCL_ERROR;
- }
- if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &info) ){
- Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[1]), 0);
- return TCL_ERROR;
- }
- db = *(sqlite3 **)info.objClientData;
- pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset);
- ctx.pConflictScript = objv[3];
- ctx.pFilterScript = objc==5 ? objv[4] : 0;
- ctx.interp = interp;
- if( sStr.nStream==0 ){
- if( bV2==0 ){
- rc = sqlite3changeset_apply(db, (int)nChangeset, pChangeset,
- (objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx
- );
- }else{
- rc = sqlite3changeset_apply_v2(db, (int)nChangeset, pChangeset,
- (objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx,
- &pRebase, &nRebase, flags
- );
- }
- }else{
- sStr.aData = (unsigned char*)pChangeset;
- sStr.nData = (int)nChangeset;
- if( bV2==0 ){
- rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
- (objc==5) ? test_filter_handler : 0,
- test_conflict_handler, (void *)&ctx
- );
- }else{
- rc = sqlite3changeset_apply_v2_strm(db, testStreamInput, (void*)&sStr,
- (objc==5) ? test_filter_handler : 0,
- test_conflict_handler, (void *)&ctx,
- &pRebase, &nRebase, flags
- );
- }
- }
- if( rc!=SQLITE_OK ){
- return test_session_error(interp, rc, 0);
- }else{
- Tcl_ResetResult(interp);
- if( bV2 && pRebase ){
- Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pRebase, nRebase));
- }
- }
- sqlite3_free(pRebase);
- return TCL_OK;
- }
- /*
- ** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
- */
- static int SQLITE_TCLAPI test_sqlite3changeset_apply(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- return testSqlite3changesetApply(0, clientData, interp, objc, objv);
- }
- /*
- ** sqlite3changeset_apply_v2 DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
- */
- static int SQLITE_TCLAPI test_sqlite3changeset_apply_v2(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- return testSqlite3changesetApply(1, clientData, interp, objc, objv);
- }
- /*
- ** sqlite3changeset_apply_replace_all DB CHANGESET
- */
- static int SQLITE_TCLAPI test_sqlite3changeset_apply_replace_all(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- sqlite3 *db; /* Database handle */
- Tcl_CmdInfo info; /* Database Tcl command (objv[1]) info */
- int rc; /* Return code from changeset_invert() */
- void *pChangeset; /* Buffer containing changeset */
- Tcl_Size nChangeset; /* Size of buffer aChangeset in bytes */
- if( objc!=3 ){
- Tcl_WrongNumArgs(interp, 1, objv, "DB CHANGESET");
- return TCL_ERROR;
- }
- if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &info) ){
- Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0);
- return TCL_ERROR;
- }
- db = *(sqlite3 **)info.objClientData;
- pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset);
- rc = sqlite3changeset_apply(db, (int)nChangeset, pChangeset,
- 0, replace_handler,0);
- if( rc!=SQLITE_OK ){
- return test_session_error(interp, rc, 0);
- }
- Tcl_ResetResult(interp);
- return TCL_OK;
- }
- /*
- ** sqlite3changeset_invert CHANGESET
- */
- static int SQLITE_TCLAPI test_sqlite3changeset_invert(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- int rc; /* Return code from changeset_invert() */
- Tcl_Size nn;
- TestStreamInput sIn; /* Input stream */
- TestSessionsBlob sOut; /* Output blob */
- if( objc!=2 ){
- Tcl_WrongNumArgs(interp, 1, objv, "CHANGESET");
- return TCL_ERROR;
- }
- memset(&sIn, 0, sizeof(sIn));
- memset(&sOut, 0, sizeof(sOut));
- sIn.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
- sIn.aData = Tcl_GetByteArrayFromObj(objv[1], &nn);
- sIn.nData = (int)nn;
- if( sIn.nStream ){
- rc = sqlite3changeset_invert_strm(
- testStreamInput, (void*)&sIn, testStreamOutput, (void*)&sOut
- );
- }else{
- rc = sqlite3changeset_invert(sIn.nData, sIn.aData, &sOut.n, &sOut.p);
- }
- if( rc!=SQLITE_OK ){
- rc = test_session_error(interp, rc, 0);
- }else{
- assert_changeset_is_ok(sOut.n, sOut.p);
- Tcl_SetObjResult(interp,Tcl_NewByteArrayObj((unsigned char*)sOut.p,sOut.n));
- }
- sqlite3_free(sOut.p);
- return rc;
- }
- /*
- ** sqlite3changeset_concat LEFT RIGHT
- */
- static int SQLITE_TCLAPI test_sqlite3changeset_concat(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- int rc; /* Return code from changeset_invert() */
- Tcl_Size nn;
- TestStreamInput sLeft; /* Input stream */
- TestStreamInput sRight; /* Input stream */
- TestSessionsBlob sOut = {0,0}; /* Output blob */
- if( objc!=3 ){
- Tcl_WrongNumArgs(interp, 1, objv, "LEFT RIGHT");
- return TCL_ERROR;
- }
- memset(&sLeft, 0, sizeof(sLeft));
- memset(&sRight, 0, sizeof(sRight));
- sLeft.aData = Tcl_GetByteArrayFromObj(objv[1], &nn);
- sLeft.nData = (int)nn;
- sRight.aData = Tcl_GetByteArrayFromObj(objv[2], &nn);
- sRight.nData = (int)nn;
- sLeft.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
- sRight.nStream = sLeft.nStream;
- if( sLeft.nStream>0 ){
- rc = sqlite3changeset_concat_strm(
- testStreamInput, (void*)&sLeft,
- testStreamInput, (void*)&sRight,
- testStreamOutput, (void*)&sOut
- );
- }else{
- rc = sqlite3changeset_concat(
- sLeft.nData, sLeft.aData, sRight.nData, sRight.aData, &sOut.n, &sOut.p
- );
- }
- if( rc!=SQLITE_OK ){
- rc = test_session_error(interp, rc, 0);
- }else{
- assert_changeset_is_ok(sOut.n, sOut.p);
- Tcl_SetObjResult(interp,Tcl_NewByteArrayObj((unsigned char*)sOut.p,sOut.n));
- }
- sqlite3_free(sOut.p);
- return rc;
- }
- static Tcl_Obj *testIterData(sqlite3_changeset_iter *pIter){
- Tcl_Obj *pVar = 0;
- int nCol; /* Number of columns in table */
- int nCol2; /* Number of columns in table */
- int op; /* SQLITE_INSERT, UPDATE or DELETE */
- const char *zTab; /* Name of table change applies to */
- Tcl_Obj *pOld; /* Vector of old.* values */
- Tcl_Obj *pNew; /* Vector of new.* values */
- int bIndirect;
-
- char *zPK;
- unsigned char *abPK;
- int i;
- sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
- pVar = Tcl_NewObj();
- Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
- op==SQLITE_INSERT ? "INSERT" :
- op==SQLITE_UPDATE ? "UPDATE" :
- "DELETE", -1
- ));
- Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
- Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
- zPK = ckalloc(nCol+1);
- memset(zPK, 0, nCol+1);
- sqlite3changeset_pk(pIter, &abPK, &nCol2);
- assert( nCol==nCol2 );
- for(i=0; i<nCol; i++){
- zPK[i] = (abPK[i] ? 'X' : '.');
- }
- Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
- ckfree(zPK);
- pOld = Tcl_NewObj();
- if( op!=SQLITE_INSERT ){
- for(i=0; i<nCol; i++){
- sqlite3_value *pVal;
- sqlite3changeset_old(pIter, i, &pVal);
- test_append_value(pOld, pVal);
- }
- }
- pNew = Tcl_NewObj();
- if( op!=SQLITE_DELETE ){
- for(i=0; i<nCol; i++){
- sqlite3_value *pVal;
- sqlite3changeset_new(pIter, i, &pVal);
- test_append_value(pNew, pVal);
- }
- }
- Tcl_ListObjAppendElement(0, pVar, pOld);
- Tcl_ListObjAppendElement(0, pVar, pNew);
- return pVar;
- }
- /*
- ** sqlite3session_foreach VARNAME CHANGESET SCRIPT
- */
- static int SQLITE_TCLAPI test_sqlite3session_foreach(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- void *pChangeset;
- Tcl_Size nChangeset;
- sqlite3_changeset_iter *pIter;
- int rc;
- Tcl_Obj *pVarname;
- Tcl_Obj *pCS;
- Tcl_Obj *pScript;
- int isCheckNext = 0;
- int isInvert = 0;
- TestStreamInput sStr;
- memset(&sStr, 0, sizeof(sStr));
- while( objc>1 ){
- char *zOpt = Tcl_GetString(objv[1]);
- int nOpt = (int)strlen(zOpt);
- if( zOpt[0]!='-' ) break;
- if( nOpt<=7 && 0==sqlite3_strnicmp(zOpt, "-invert", nOpt) ){
- isInvert = 1;
- }else
- if( nOpt<=5 && 0==sqlite3_strnicmp(zOpt, "-next", nOpt) ){
- isCheckNext = 1;
- }else{
- break;
- }
- objv++;
- objc--;
- }
- if( objc!=4 ){
- Tcl_WrongNumArgs(
- interp, 1, objv, "?-next? ?-invert? VARNAME CHANGESET SCRIPT");
- return TCL_ERROR;
- }
- pVarname = objv[1];
- pCS = objv[2];
- pScript = objv[3];
- pChangeset = (void *)Tcl_GetByteArrayFromObj(pCS, &nChangeset);
- sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
- if( isInvert ){
- int f = SQLITE_CHANGESETSTART_INVERT;
- if( sStr.nStream==0 ){
- rc = sqlite3changeset_start_v2(&pIter, (int)nChangeset, pChangeset, f);
- }else{
- void *pCtx = (void*)&sStr;
- sStr.aData = (unsigned char*)pChangeset;
- sStr.nData = (int)nChangeset;
- rc = sqlite3changeset_start_v2_strm(&pIter, testStreamInput, pCtx, f);
- }
- }else{
- if( sStr.nStream==0 ){
- rc = sqlite3changeset_start(&pIter, (int)nChangeset, pChangeset);
- }else{
- sStr.aData = (unsigned char*)pChangeset;
- sStr.nData = (int)nChangeset;
- rc = sqlite3changeset_start_strm(&pIter, testStreamInput, (void*)&sStr);
- }
- }
- if( rc!=SQLITE_OK ){
- return test_session_error(interp, rc, 0);
- }
- while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
- Tcl_Obj *pVar = 0; /* Tcl value to set $VARNAME to */
- pVar = testIterData(pIter);
- Tcl_ObjSetVar2(interp, pVarname, 0, pVar, 0);
- rc = Tcl_EvalObjEx(interp, pScript, 0);
- if( rc!=TCL_OK && rc!=TCL_CONTINUE ){
- sqlite3changeset_finalize(pIter);
- return rc==TCL_BREAK ? TCL_OK : rc;
- }
- }
- if( isCheckNext ){
- int rc2 = sqlite3changeset_next(pIter);
- rc = sqlite3changeset_finalize(pIter);
- assert( (rc2==SQLITE_DONE && rc==SQLITE_OK) || rc2==rc );
- }else{
- rc = sqlite3changeset_finalize(pIter);
- }
- if( rc!=SQLITE_OK ){
- return test_session_error(interp, rc, 0);
- }
- return TCL_OK;
- }
- /*
- ** tclcmd: CMD configure REBASE-BLOB
- ** tclcmd: CMD rebase CHANGESET
- ** tclcmd: CMD delete
- */
- static int SQLITE_TCLAPI test_rebaser_cmd(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- static struct RebaseSubcmd {
- const char *zSub;
- int nArg;
- const char *zMsg;
- int iSub;
- } aSub[] = {
- { "configure", 1, "REBASE-BLOB" }, /* 0 */
- { "delete", 0, "" }, /* 1 */
- { "rebase", 1, "CHANGESET" }, /* 2 */
- { 0 }
- };
- sqlite3_rebaser *p = (sqlite3_rebaser*)clientData;
- int iSub;
- int rc;
- if( objc<2 ){
- Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
- return TCL_ERROR;
- }
- rc = Tcl_GetIndexFromObjStruct(interp,
- objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
- );
- if( rc!=TCL_OK ) return rc;
- if( objc!=2+aSub[iSub].nArg ){
- Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
- return TCL_ERROR;
- }
- assert( iSub==0 || iSub==1 || iSub==2 );
- assert( rc==SQLITE_OK );
- switch( iSub ){
- case 0: { /* configure */
- Tcl_Size nRebase = 0;
- unsigned char *pRebase = Tcl_GetByteArrayFromObj(objv[2], &nRebase);
- rc = sqlite3rebaser_configure(p, (int)nRebase, pRebase);
- break;
- }
- case 1: /* delete */
- Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
- break;
- default: { /* rebase */
- TestStreamInput sStr; /* Input stream */
- TestSessionsBlob sOut; /* Output blob */
- Tcl_Size nn;
- memset(&sStr, 0, sizeof(sStr));
- memset(&sOut, 0, sizeof(sOut));
- sStr.aData = Tcl_GetByteArrayFromObj(objv[2], &nn);
- sStr.nData = nn;
- sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
- if( sStr.nStream ){
- rc = sqlite3rebaser_rebase_strm(p,
- testStreamInput, (void*)&sStr,
- testStreamOutput, (void*)&sOut
- );
- }else{
- rc = sqlite3rebaser_rebase(p, sStr.nData, sStr.aData, &sOut.n, &sOut.p);
- }
- if( rc==SQLITE_OK ){
- assert_changeset_is_ok(sOut.n, sOut.p);
- Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(sOut.p, sOut.n));
- }
- sqlite3_free(sOut.p);
- break;
- }
- }
- if( rc!=SQLITE_OK ){
- return test_session_error(interp, rc, 0);
- }
- return TCL_OK;
- }
- static void SQLITE_TCLAPI test_rebaser_del(void *clientData){
- sqlite3_rebaser *p = (sqlite3_rebaser*)clientData;
- sqlite3rebaser_delete(p);
- }
- /*
- ** tclcmd: sqlite3rebaser_create NAME
- */
- static int SQLITE_TCLAPI test_sqlite3rebaser_create(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- int rc;
- sqlite3_rebaser *pNew = 0;
- if( objc!=2 ){
- Tcl_WrongNumArgs(interp, 1, objv, "NAME");
- return SQLITE_ERROR;
- }
- rc = sqlite3rebaser_create(&pNew);
- if( rc!=SQLITE_OK ){
- return test_session_error(interp, rc, 0);
- }
- Tcl_CreateObjCommand(interp, Tcl_GetString(objv[1]), test_rebaser_cmd,
- (ClientData)pNew, test_rebaser_del
- );
- Tcl_SetObjResult(interp, objv[1]);
- return TCL_OK;
- }
- /*
- ** Run some sanity checks on the changeset in nChangeset byte buffer
- ** pChangeset. If any fail, return a non-zero value and, optionally,
- ** set output variable (*pzErr) to point to a buffer containing an
- ** English language error message describing the problem. In this
- ** case it is the responsibility of the caller to free the buffer
- ** using sqlite3_free().
- **
- ** Or, if the changeset appears to be well-formed, this function
- ** returns SQLITE_OK and sets (*pzErr) to NULL.
- */
- static int sqlite3_test_changeset(
- int nChangeset,
- void *pChangeset,
- char **pzErr
- ){
- sqlite3_changeset_iter *pIter = 0;
- char *zErr = 0;
- int rc = SQLITE_OK;
- int bPatch = (nChangeset>0 && ((char*)pChangeset)[0]=='P');
- rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
- if( rc==SQLITE_OK ){
- int rc2;
- while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter) ){
- unsigned char *aPk = 0;
- int nCol = 0;
- int op = 0;
- const char *zTab = 0;
- sqlite3changeset_pk(pIter, &aPk, &nCol);
- sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
- if( op==SQLITE_UPDATE ){
- int iCol;
- for(iCol=0; iCol<nCol; iCol++){
- sqlite3_value *pNew = 0;
- sqlite3_value *pOld = 0;
- sqlite3changeset_new(pIter, iCol, &pNew);
- sqlite3changeset_old(pIter, iCol, &pOld);
- if( aPk[iCol] ){
- if( pOld==0 ) rc = SQLITE_ERROR;
- }else if( bPatch ){
- if( pOld ) rc = SQLITE_ERROR;
- }else{
- if( (pOld==0)!=(pNew==0) ) rc = SQLITE_ERROR;
- }
- if( rc!=SQLITE_OK ){
- zErr = sqlite3_mprintf(
- "unexpected SQLITE_UPDATE (bPatch=%d pk=%d pOld=%d pNew=%d)",
- bPatch, (int)aPk[iCol], pOld!=0, pNew!=0
- );
- break;
- }
- }
- }
- }
- rc2 = sqlite3changeset_finalize(pIter);
- if( rc==SQLITE_OK ){
- rc = rc2;
- }
- }
- *pzErr = zErr;
- return rc;
- }
- /*
- ** test_changeset CHANGESET
- */
- static int SQLITE_TCLAPI test_changeset(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- void *pChangeset = 0; /* Buffer containing changeset */
- Tcl_Size nChangeset = 0; /* Size of buffer aChangeset in bytes */
- int rc = SQLITE_OK;
- char *z = 0;
- if( objc!=2 ){
- Tcl_WrongNumArgs(interp, 1, objv, "CHANGESET");
- return TCL_ERROR;
- }
- pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[1], &nChangeset);
- Tcl_ResetResult(interp);
- rc = sqlite3_test_changeset((int)nChangeset, pChangeset, &z);
- if( rc!=SQLITE_OK ){
- char *zErr = sqlite3_mprintf("(%d) - \"%s\"", rc, z);
- Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
- sqlite3_free(zErr);
- }
- sqlite3_free(z);
- return rc ? TCL_ERROR : TCL_OK;
- }
- /*
- ** tclcmd: sqlite3rebaser_configure OP VALUE
- */
- static int SQLITE_TCLAPI test_sqlite3session_config(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- static struct ConfigOpt {
- const char *zSub;
- int op;
- } aSub[] = {
- { "strm_size", SQLITE_SESSION_CONFIG_STRMSIZE },
- { "invalid", 0 },
- { 0 }
- };
- int rc;
- int iSub;
- int iVal;
- if( objc!=3 ){
- Tcl_WrongNumArgs(interp, 1, objv, "OP VALUE");
- return SQLITE_ERROR;
- }
- rc = Tcl_GetIndexFromObjStruct(interp,
- objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
- );
- if( rc!=TCL_OK ) return rc;
- if( Tcl_GetIntFromObj(interp, objv[2], &iVal) ) return TCL_ERROR;
- rc = sqlite3session_config(aSub[iSub].op, (void*)&iVal);
- if( rc!=SQLITE_OK ){
- return test_session_error(interp, rc, 0);
- }
- Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal));
- return TCL_OK;
- }
- typedef struct TestChangegroup TestChangegroup;
- struct TestChangegroup {
- sqlite3_changegroup *pGrp;
- };
- typedef struct TestChangeIter TestChangeIter;
- struct TestChangeIter {
- sqlite3_changeset_iter *pIter;
- };
- /*
- ** Destructor for Tcl changegroup command object.
- */
- static void test_changegroup_del(void *clientData){
- TestChangegroup *pGrp = (TestChangegroup*)clientData;
- sqlite3changegroup_delete(pGrp->pGrp);
- ckfree(pGrp);
- }
- /*
- ** Tclcmd: $changegroup schema DB DBNAME
- ** Tclcmd: $changegroup add CHANGESET
- ** Tclcmd: $changegroup output
- ** Tclcmd: $changegroup delete
- */
- static int SQLITE_TCLAPI test_changegroup_cmd(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- TestChangegroup *p = (TestChangegroup*)clientData;
- static struct ChangegroupCmd {
- const char *zSub;
- int nArg;
- const char *zMsg;
- int iSub;
- } aSub[] = {
- { "schema", 2, "DB DBNAME", }, /* 0 */
- { "add", 1, "CHANGESET", }, /* 1 */
- { "output", 0, "", }, /* 2 */
- { "delete", 0, "", }, /* 3 */
- { "add_change", 1, "ITERATOR", }, /* 4 */
- { 0 }
- };
- int rc = TCL_OK;
- int iSub = 0;
- if( objc<2 ){
- Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
- return TCL_ERROR;
- }
- rc = Tcl_GetIndexFromObjStruct(interp,
- objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
- );
- if( rc!=TCL_OK ) return rc;
- if( objc!=2+aSub[iSub].nArg ){
- Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
- return TCL_ERROR;
- }
- switch( iSub ){
- case 0: { /* schema */
- sqlite3 *db = 0;
- const char *zDb = Tcl_GetString(objv[3]);
- if( dbHandleFromObj(interp, objv[2], &db) ){
- return TCL_ERROR;
- }
- rc = sqlite3changegroup_schema(p->pGrp, db, zDb);
- if( rc!=SQLITE_OK ) rc = test_session_error(interp, rc, 0);
- break;
- };
- case 1: { /* add */
- Tcl_Size nByte = 0;
- const u8 *aByte = Tcl_GetByteArrayFromObj(objv[2], &nByte);
- rc = sqlite3changegroup_add(p->pGrp, (int)nByte, (void*)aByte);
- if( rc!=SQLITE_OK ) rc = test_session_error(interp, rc, 0);
- break;
- };
- case 2: { /* output */
- int nByte = 0;
- u8 *aByte = 0;
- rc = sqlite3changegroup_output(p->pGrp, &nByte, (void**)&aByte);
- if( rc!=SQLITE_OK ){
- rc = test_session_error(interp, rc, 0);
- }else{
- Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(aByte, nByte));
- }
- sqlite3_free(aByte);
- break;
- };
- case 4: { /* add_change */
- Tcl_CmdInfo cmdInfo; /* Database Tcl command (objv[2]) info */
- TestChangeIter *pIter = 0;
- const char *zIter = Tcl_GetString(objv[2]);
- if( 0==Tcl_GetCommandInfo(interp, zIter, &cmdInfo) ){
- Tcl_AppendResult(interp, "no such iter: ", Tcl_GetString(objv[2]), 0);
- return TCL_ERROR;
- }
- pIter = (struct TestChangeIter*)cmdInfo.objClientData;
- rc = sqlite3changegroup_add_change(p->pGrp, pIter->pIter);
- if( rc!=SQLITE_OK ){
- rc = test_session_error(interp, rc, 0);
- }
- break;
- };
- default: { /* delete */
- assert( iSub==3 );
- Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
- break;
- }
- }
- return rc;
- }
- /*
- ** Tclcmd: sqlite3changegroup CMD
- */
- static int SQLITE_TCLAPI test_sqlite3changegroup(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- int rc; /* sqlite3changegroup_new() return code */
- TestChangegroup *p; /* New wrapper object */
- if( objc!=2 ){
- Tcl_WrongNumArgs(interp, 1, objv, "CMD");
- return TCL_ERROR;
- }
- p = (TestChangegroup*)ckalloc(sizeof(TestChangegroup));
- memset(p, 0, sizeof(TestChangegroup));
- rc = sqlite3changegroup_new(&p->pGrp);
- if( rc!=SQLITE_OK ){
- ckfree((char*)p);
- return test_session_error(interp, rc, 0);
- }
- Tcl_CreateObjCommand(
- interp, Tcl_GetString(objv[1]), test_changegroup_cmd, (ClientData)p,
- test_changegroup_del
- );
- Tcl_SetObjResult(interp, objv[1]);
- return TCL_OK;
- }
- extern const char *sqlite3ErrName(int);
- /*
- ** Destructor for Tcl iterator command object.
- */
- static void test_iter_del(void *clientData){
- TestChangeIter *p = (TestChangeIter*)clientData;
- sqlite3changeset_finalize(p->pIter);
- ckfree(p);
- }
- static int SQLITE_TCLAPI test_iter_cmd(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- static const char *aSub[] = {
- "next", /* 0 */
- "data", /* 1 */
- "finalize", /* 2 */
- 0
- };
- int iSub = 0;
- TestChangeIter *p = (TestChangeIter*)clientData;
- int rc = SQLITE_OK;
- if( objc<2 ){
- Tcl_WrongNumArgs(interp, 1, objv, "CMD");
- return TCL_ERROR;
- }
- if( Tcl_GetIndexFromObj(interp, objv[1], aSub, "sub-command", 0, &iSub) ){
- return TCL_ERROR;
- }
- switch( iSub ){
- case 0:
- rc = sqlite3changeset_next(p->pIter);
- Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
- break;
- case 1:
- Tcl_SetObjResult(interp, testIterData(p->pIter));
- break;
- case 2:
- rc = sqlite3changeset_finalize(p->pIter);
- p->pIter = 0;
- Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
- Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
- break;
- default:
- assert( 0 );
- break;
- }
- return TCL_OK;
- }
- /*
- ** Tclcmd: sqlite3changeset_start ?-invert? CHANGESET
- */
- static int SQLITE_TCLAPI test_sqlite3changeset_start(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
- ){
- int isInvert = 0;
- void *pChangeset = 0; /* Buffer containing changeset */
- Tcl_Size nChangeset = 0; /* Size of buffer aChangeset in bytes */
- TestChangeIter *pNew = 0;
- sqlite3_changeset_iter *pIter = 0;
- int flags = 0;
- int rc = SQLITE_OK;
- static int iCmd = 1;
- char zCmd[64];
- if( objc==3 ){
- Tcl_Size n = 0;
- const char *z = Tcl_GetStringFromObj(objv[1], &n);
- isInvert = (n>=2 && sqlite3_strnicmp(z, "-invert", (int)n)==0);
- }
- if( objc!=2 && (objc!=3 || !isInvert) ){
- Tcl_WrongNumArgs(interp, 1, objv, "?-invert? CHANGESET");
- return TCL_ERROR;
- }
- flags = isInvert ? SQLITE_CHANGESETSTART_INVERT : 0;
- pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[objc-1], &nChangeset);
- rc = sqlite3changeset_start_v2(&pIter, (int)nChangeset, pChangeset, flags);
- if( rc!=SQLITE_OK ){
- char *zErr = sqlite3_mprintf(
- "error in sqlite3changeset_start_v2() - %d", rc
- );
- Tcl_AppendResult(interp, zErr, (char*)0);
- return TCL_ERROR;
- }
- pNew = (TestChangeIter*)ckalloc(sizeof(TestChangeIter));
- pNew->pIter = pIter;
- sprintf(zCmd, "csiter%d", iCmd++);
- Tcl_CreateObjCommand(interp, zCmd, test_iter_cmd, (void*)pNew, test_iter_del);
- Tcl_SetObjResult(interp, Tcl_NewStringObj(zCmd, -1));
- return TCL_OK;
- }
- int TestSession_Init(Tcl_Interp *interp){
- struct Cmd {
- const char *zCmd;
- Tcl_ObjCmdProc *xProc;
- } aCmd[] = {
- { "sqlite3session", test_sqlite3session },
- { "sqlite3changegroup", test_sqlite3changegroup },
- { "sqlite3changeset_start", test_sqlite3changeset_start },
- { "sqlite3session_foreach", test_sqlite3session_foreach },
- { "sqlite3changeset_invert", test_sqlite3changeset_invert },
- { "sqlite3changeset_concat", test_sqlite3changeset_concat },
- { "sqlite3changeset_apply", test_sqlite3changeset_apply },
- { "sqlite3changeset_apply_v2", test_sqlite3changeset_apply_v2 },
- { "sqlite3changeset_apply_replace_all",
- test_sqlite3changeset_apply_replace_all },
- { "sql_exec_changeset", test_sql_exec_changeset },
- { "sqlite3rebaser_create", test_sqlite3rebaser_create },
- { "sqlite3session_config", test_sqlite3session_config },
- { "test_changeset", test_changeset },
- };
- int i;
- for(i=0; i<sizeof(aCmd)/sizeof(struct Cmd); i++){
- struct Cmd *p = &aCmd[i];
- Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, 0, 0);
- }
- return TCL_OK;
- }
- #endif /* SQLITE_TEST && SQLITE_SESSION && SQLITE_PREUPDATE_HOOK */
|