From f1b40e830544fa64f065d06787c9fa9b73a2ff48 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 15 Mar 2018 19:25:40 +0000 Subject: [PATCH] Add simple tests for the sessions module rebase API. FossilOrigin-Name: cf0d1abb44cf170d747e9c11f49ec03a29f00ab4821c613ca1e05b883a568211 --- ext/session/sessionrebase.test | 126 +++++++++++++++++++++++++++++++++ ext/session/sqlite3session.c | 29 ++++++-- ext/session/sqlite3session.h | 2 +- ext/session/test_session.c | 124 ++++++++++++++++++++++++++++++++ manifest | 18 ++--- manifest.uuid | 2 +- 6 files changed, 283 insertions(+), 18 deletions(-) diff --git a/ext/session/sessionrebase.test b/ext/session/sessionrebase.test index 403b3320d0..9085068922 100644 --- a/ext/session/sessionrebase.test +++ b/ext/session/sessionrebase.test @@ -121,5 +121,131 @@ do_apply_v2_test 1.3.2 { {DELETE t1 0 X. {i 1 {} {}} {}} } +#------------------------------------------------------------------------- +# Test cases 2.* - simple tests of rebasing actual changesets. +# +# 2.1.1 - 1u2u1r +# 2.1.2 - 1u2u2r +# 2.1.3 - 1d2d +# 2.1.4 - 1d2u1r +# 2.1.5 - 1d2u2r !! +# 2.1.6 - 1u2d1r + +proc xConflictAbort {args} { + return "ABORT" +} + +# Take a copy of database test.db in file test.db2. Execute $sql1 +# against test.db and $sql2 against test.db2. Capture a changeset +# for each. Then send the test.db2 changeset to test.db and apply +# it with the conflict handlers in $conflict_handler. Patch the +# test.db changeset and then execute it against test.db2. Test that +# the two databases come out the same. +# +proc do_rebase_test {tn sql1 sql2 conflict_handler {testsql ""} {testres ""}} { + + forcedelete test.db2 test.db2-journal test.db2-wal + forcecopy test.db test.db2 + sqlite3 db2 test.db2 + + db eval BEGIN + + sqlite3session S1 db main + S1 attach * + execsql $sql1 db + set c1 [S1 changeset] + S1 delete + + sqlite3session S2 db2 main + S2 attach * + execsql $sql2 db2 + set c2 [S2 changeset] + S2 delete + + set ::lConflict $conflict_handler + set rebase [sqlite3changeset_apply_v2 db $c2 xConflict] + #puts [changeset_to_list $rebase] + + sqlite3rebaser_create R + R configure $rebase + set c1r [R rebase $c1] + R delete + #puts [changeset_to_list $c1r] + + sqlite3changeset_apply_v2 db2 $c1r xConflictAbort + + uplevel [list do_test $tn.1 [list compare_db db db2] {}] + db2 close + + if {$testsql!=""} { + uplevel [list do_execsql_test $tn.2 $testsql $testres] + } + + db eval ROLLBACK +} + +reset_db +do_execsql_test 2.1.0 { + CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); + INSERT INTO t1 VALUES(3, 'three'); +} +do_rebase_test 2.1.1 { + UPDATE t1 SET b = 'two.1' WHERE a=2; +} { + UPDATE t1 SET b = 'two.2' WHERE a=2; +} { + OMIT +} { SELECT * FROM t1 } {1 one 2 two.1 3 three} + +do_rebase_test 2.1.2 { + UPDATE t1 SET b = 'two.1' WHERE a=2; +} { + UPDATE t1 SET b = 'two.2' WHERE a=2; +} { + REPLACE +} { SELECT * FROM t1 } {1 one 2 two.2 3 three} + +do_rebase_test 2.1.3 { + DELETE FROM t1 WHERE a=3; +} { + DELETE FROM t1 WHERE a=3; +} { + OMIT +} { SELECT * FROM t1 } {1 one 2 two} + +do_rebase_test 2.1.4 { + DELETE FROM t1 WHERE a=1; +} { + UPDATE t1 SET b='one.2' WHERE a=1 +} { + OMIT +} { SELECT * FROM t1 } {2 two 3 three} + +#do_rebase_test 2.1.5 { + #DELETE FROM t1 WHERE a=1; +#} { + #UPDATE t1 SET b='one.2' WHERE a=1 +#} { + #REPLACE +#} { SELECT * FROM t1 } {2 two 3 three} + +do_rebase_test 2.1.6 { + UPDATE t1 SET b='three.1' WHERE a=3; +} { + DELETE FROM t1 WHERE a=3; +} { + OMIT +} { SELECT * FROM t1 } {1 one 2 two 3 three.1} + +do_rebase_test 2.1.7 { + UPDATE t1 SET b='three.1' WHERE a=3; +} { + DELETE FROM t1 WHERE a=3; +} { + REPLACE +} { SELECT * FROM t1 } {1 one 2 two} finish_test + diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index ad898e8844..45803c7b2c 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -4278,10 +4278,18 @@ static int sessionChangesetApply( sqlite3_finalize(sApply.pUpdate); sqlite3_finalize(sApply.pInsert); sqlite3_finalize(sApply.pSelect); - memset(&sApply, 0, sizeof(sApply)); sApply.db = db; + sApply.pDelete = 0; + sApply.pUpdate = 0; + sApply.pInsert = 0; + sApply.pSelect = 0; + sApply.nCol = 0; + sApply.azCol = 0; + sApply.abPK = 0; + sApply.bStat1 = 0; sApply.bDeferConstraints = 1; sApply.bRebaseStarted = 0; + memset(&sApply.constraints, 0, sizeof(SessionBuffer)); /* If an xFilter() callback was specified, invoke it now. If the ** xFilter callback returns zero, skip this table. If it returns @@ -4658,7 +4666,6 @@ static int sessionChangesetToHash( int rc = SQLITE_OK; SessionTable *pTab = 0; - while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){ const char *zNew; int nCol; @@ -5010,9 +5017,12 @@ static void sessionAppendRecordMerge( pOut += nn1; } } - a1 += n1; - a2 += n2; + a1 += nn1; + a2 += nn2; } + + pBuf->nBuf = pOut-pBuf->aBuf; + assert( pBuf->nBuf<=pBuf->nAlloc ); } } @@ -5031,7 +5041,7 @@ static int sessionRebase( SessionTable *pTab = 0; SessionBuffer sOut = {0,0,0}; - while( SQLITE_OK==sessionChangesetNext(pIter, &aRec, &nRec, &bNew) ){ + while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, &bNew) ){ SessionChange *pChange = 0; if( bNew ){ @@ -5078,8 +5088,11 @@ static int sessionRebase( if( pIter->op==SQLITE_INSERT ){ sessionAppendBlob(&sOut, aRec, nRec, &rc); }else{ + u8 *pCsr = aRec; + sessionSkipRecord(&pCsr, pIter->nCol); sessionAppendRecordMerge(&sOut, pIter->nCol, 1, - aRec, nRec, pChange->aRecord, pChange->nRecord, &rc + pCsr, nRec-(pCsr-aRec), + pChange->aRecord, pChange->nRecord, &rc ); } } @@ -5143,6 +5156,8 @@ int sqlite3rebaser_create(sqlite3_rebaser **ppNew){ pNew = sqlite3_malloc(sizeof(sqlite3_rebaser)); if( pNew==0 ){ rc = SQLITE_NOMEM; + }else{ + memset(pNew, 0, sizeof(sqlite3_rebaser)); } *ppNew = pNew; return rc; @@ -5208,7 +5223,7 @@ int sqlite3rebaser_rebase_strm( /* ** Destroy a rebaser object */ -void sqlite3rebaser_destroy(sqlite3_rebaser *p){ +void sqlite3rebaser_delete(sqlite3_rebaser *p){ if( p ){ sessionDeleteTable(p->grp.pList); sqlite3_free(p); diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index a7281bd4e7..ee002b008f 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -1235,7 +1235,7 @@ int sqlite3rebaser_rebase( ); /* Destroy a rebaser object */ -void sqlite3rebaser_destroy(sqlite3_rebaser *p); +void sqlite3rebaser_delete(sqlite3_rebaser *p); /* ** CAPI3REF: Streaming Versions of API functions. diff --git a/ext/session/test_session.c b/ext/session/test_session.c index 7b08267b53..bdd144b5fc 100644 --- a/ext/session/test_session.c +++ b/ext/session/test_session.c @@ -14,6 +14,10 @@ # endif #endif +#ifndef SQLITE_AMALGAMATION + typedef unsigned char u8; +#endif + typedef struct TestSession TestSession; struct TestSession { sqlite3_session *pSession; @@ -1063,6 +1067,125 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach( 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[] +){ + 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 */ + int nRebase = 0; + unsigned char *pRebase = Tcl_GetByteArrayFromObj(objv[2], &nRebase); + rc = sqlite3rebaser_configure(p, nRebase, pRebase); + break; + } + + case 1: /* delete */ + Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); + break; + + default: { /* rebase */ + TestStreamInput sStr; /* Input stream */ + TestSessionsBlob sOut; /* Output blob */ + + memset(&sStr, 0, sizeof(sStr)); + memset(&sOut, 0, sizeof(sOut)); + sStr.aData = Tcl_GetByteArrayFromObj(objv[2], &sStr.nData); + 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 ){ + 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; +} + int TestSession_Init(Tcl_Interp *interp){ struct Cmd { const char *zCmd; @@ -1077,6 +1200,7 @@ int TestSession_Init(Tcl_Interp *interp){ { "sqlite3changeset_apply_replace_all", test_sqlite3changeset_apply_replace_all }, { "sql_exec_changeset", test_sql_exec_changeset }, + { "sqlite3rebaser_create", test_sqlite3rebaser_create }, }; int i; diff --git a/manifest b/manifest index b887be3c0a..4948fb7f53 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\slargely\suntested\sAPIs\sfor\srebasing\schangesets. -D 2018-03-14T21:06:58.004 +C Add\ssimple\stests\sfor\sthe\ssessions\smodule\srebase\sAPI. +D 2018-03-15T19:25:40.859 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 7016fc56c6b9bfe5daac4f34be8be38d8c0b5fab79ccbfb764d3b23bf1c6fff3 @@ -400,12 +400,12 @@ F ext/session/sessionat.test efe88965e74ff1bc2af9c310b28358c02d420c1fb2705cc7a28 F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7 F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0 -F ext/session/sessionrebase.test b4ac7545e3c69deaeab061c2bf36ad9e99aa6c38db94c340d7e48a230a9d4be8 +F ext/session/sessionrebase.test d3a33c733e5564afe517252167d8f456a04601b047246d85f1e84bf319c2897f F ext/session/sessionstat1.test 41cd97c2e48619a41cdf8ae749e1b25f34719de638689221aa43971be693bf4e F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc -F ext/session/sqlite3session.c 16561ad7eb8270bd3d2ee42c434043e68831bbd452fbea82d83922a1066a7cc8 -F ext/session/sqlite3session.h 74ba48151f3593a66a975ac095d7b53efa6c1e12fe83a903e10bf8d85a1429dd -F ext/session/test_session.c 8c04dc8cada82bd4e12f18ada3e35b56a8fd4d8dee7caac324ae28091c2b492f +F ext/session/sqlite3session.c 94b960a94d5e6b2117215a7b1057b3db74ca1222dded2a94588f8ccac5a7d929 +F ext/session/sqlite3session.h 8cb9992411344b9e906a394d2213f58da7b3942ae57e7936d1ec3fe26277dfc0 +F ext/session/test_session.c f253742ea01b089326f189b5ae15a5b55c1c9e97452e4a195ee759ba51b404d5 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f @@ -1713,7 +1713,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 445bfe977d9f3a891e08ef33237862ed047fe83e134ef3ed8b47ee0f5abd8cd6 -R 485a23056d6edbdb25721aa9069ac291 +P 39915b683b3f8d3bf872af1dede96bf2818b488a8638a1d248395023fc4bd0ef +R 749942968b245595723def17d4b1a08a U dan -Z e2563dee69381fb6fb02a6ddaa823fec +Z 85fdd8cb2cf7c970dbbe7369d1e2a257 diff --git a/manifest.uuid b/manifest.uuid index 7b1553cdcd..02df08f087 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -39915b683b3f8d3bf872af1dede96bf2818b488a8638a1d248395023fc4bd0ef \ No newline at end of file +cf0d1abb44cf170d747e9c11f49ec03a29f00ab4821c613ca1e05b883a568211 \ No newline at end of file