#if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_SESSION) #include "sqlite3session.h" #include #include #include static int test_session_error(Tcl_Interp *interp, int rc){ extern const char *sqlite3TestErrorName(int); Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3TestErrorName(rc), -1)); return TCL_ERROR; } /* ** Tclcmd: $session attach TABLE ** $session changeset ** $session delete ** $session enable BOOL */ static int test_session_cmd( void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite3_session *pSession = (sqlite3_session *)clientData; 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, "", }, /* 3 */ { 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 */ rc = sqlite3session_attach(pSession, Tcl_GetString(objv[2])); if( rc!=SQLITE_OK ){ return test_session_error(interp, rc); } break; case 1: { /* changeset */ int nChange; void *pChange; rc = sqlite3session_changeset(pSession, &nChange, &pChange); if( rc==SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pChange, nChange)); sqlite3_free(pChange); }else{ return test_session_error(interp, rc); } break; } case 2: /* delete */ Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); break; case 3: { /* enable */ int val; if( Tcl_GetBooleanFromObj(interp, objv[2], &val) ) return TCL_ERROR; val = sqlite3session_enable(pSession, val); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val)); break; } } return TCL_OK; } static void test_session_del(void *clientData){ sqlite3_session *pSession = (sqlite3_session *)clientData; sqlite3session_delete(pSession); } /* ** Tclcmd: sqlite3session CMD DB-HANDLE DB-NAME */ static int test_sqlite3session( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite3 *db; Tcl_CmdInfo info; int rc; /* sqlite3session_create() return code */ sqlite3_session *pSession; /* New session object */ 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; rc = sqlite3session_create(db, Tcl_GetString(objv[3]), &pSession); if( rc!=SQLITE_OK ){ return test_session_error(interp, rc); } Tcl_CreateObjCommand( interp, Tcl_GetString(objv[1]), test_session_cmd, (ClientData)pSession, 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: Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("t", 1)); pObj = Tcl_NewStringObj((char *)sqlite3_value_text(pVal), -1); break; case 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); } } /* ** sqlite3changeset_invert CHANGESET */ static int test_sqlite3changeset_invert( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; /* Return code from changeset_invert() */ void *aChangeset; /* Input changeset */ int nChangeSet; /* Size of buffer aChangeset in bytes */ void *aOut; /* Output changeset */ int nOut; /* Size of buffer aOut in bytes */ if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "CHANGESET"); } aChangeset = (void *)Tcl_GetByteArrayFromObj(objv[1], &nChangeSet); rc = sqlite3changeset_invert(nChangeSet, aChangeset, &nOut, &aOut); if( rc!=SQLITE_OK ){ return test_session_error(interp, rc); } Tcl_SetObjResult(interp, Tcl_NewByteArrayObj((unsigned char *)aOut, nOut)); sqlite3_free(aOut); return TCL_OK; } /* ** sqlite3session_foreach VARNAME CHANGESET SCRIPT */ static int test_sqlite3session_foreach( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ void *pChangeSet; int nChangeSet; sqlite3_changeset_iter *pIter; int rc; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "VARNAME CHANGESET SCRIPT"); return TCL_ERROR; } pChangeSet = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeSet); rc = sqlite3changeset_start(&pIter, nChangeSet, pChangeSet); if( rc!=SQLITE_OK ){ return test_session_error(interp, rc); } while( SQLITE_ROW==sqlite3changeset_next(pIter) ){ int nCol; /* Number of columns in table */ int op; /* SQLITE_INSERT, UPDATE or DELETE */ const char *zTab; /* Name of table change applies to */ Tcl_Obj *pVar; /* Tcl value to set $VARNAME to */ Tcl_Obj *pOld; /* Vector of old.* values */ Tcl_Obj *pNew; /* Vector of new.* values */ sqlite3changeset_op(pIter, &zTab, &nCol, &op); 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)); pOld = Tcl_NewObj(); if( op!=SQLITE_INSERT ){ int i; for(i=0; i