From: dan Date: Wed, 14 Mar 2018 21:06:58 +0000 (+0000) Subject: Add largely untested APIs for rebasing changesets. X-Git-Tag: version-3.23.0~36^2~10 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c0a499eaad8ca1497e321216c20797a46dd27aa1;p=thirdparty%2Fsqlite.git Add largely untested APIs for rebasing changesets. FossilOrigin-Name: 39915b683b3f8d3bf872af1dede96bf2818b488a8638a1d248395023fc4bd0ef --- diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index c8d66bb5be..ad898e8844 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -2918,7 +2918,8 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){ static int sessionChangesetNext( sqlite3_changeset_iter *p, /* Changeset iterator */ u8 **paRec, /* If non-NULL, store record pointer here */ - int *pnRec /* If non-NULL, store size of record here */ + int *pnRec, /* If non-NULL, store size of record here */ + int *pbNew /* If non-NULL, true if new table */ ){ int i; u8 op; @@ -2953,6 +2954,7 @@ static int sessionChangesetNext( op = p->in.aData[p->in.iNext++]; while( op=='T' || op=='P' ){ + if( pbNew ) *pbNew = 1; p->bPatchset = (op=='P'); if( sessionChangesetReadTblhdr(p) ) return p->rc; if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc; @@ -3031,7 +3033,7 @@ static int sessionChangesetNext( ** callback by changeset_apply(). */ int sqlite3changeset_next(sqlite3_changeset_iter *p){ - return sessionChangesetNext(p, 0, 0); + return sessionChangesetNext(p, 0, 0, 0); } /* @@ -4522,6 +4524,7 @@ struct sqlite3_changegroup { */ static int sessionChangeMerge( SessionTable *pTab, /* Table structure */ + int bRebase, /* True for a rebase hash-table */ int bPatchset, /* True for patchsets */ SessionChange *pExist, /* Existing change */ int op2, /* Second change operation */ @@ -4543,6 +4546,8 @@ static int sessionChangeMerge( pNew->nRecord = nRec; pNew->aRecord = (u8*)&pNew[1]; memcpy(pNew->aRecord, aRec, nRec); + }else if( bRebase){ + assert( 0 ); }else{ int op1 = pExist->op; @@ -4645,7 +4650,8 @@ static int sessionChangeMerge( */ static int sessionChangesetToHash( sqlite3_changeset_iter *pIter, /* Iterator to read from */ - sqlite3_changegroup *pGrp /* Changegroup object to add changeset to */ + sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ + int bRebase /* True if hash table is for rebasing */ ){ u8 *aRec; int nRec; @@ -4653,7 +4659,7 @@ static int sessionChangesetToHash( SessionTable *pTab = 0; - while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec) ){ + while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){ const char *zNew; int nCol; int op; @@ -4733,7 +4739,7 @@ static int sessionChangesetToHash( } } - rc = sessionChangeMerge(pTab, + rc = sessionChangeMerge(pTab, bRebase, pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange ); if( rc ) break; @@ -4841,7 +4847,7 @@ int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void *pData){ rc = sqlite3changeset_start(&pIter, nData, pData); if( rc==SQLITE_OK ){ - rc = sessionChangesetToHash(pIter, pGrp); + rc = sessionChangesetToHash(pIter, pGrp, 0); } sqlite3changeset_finalize(pIter); return rc; @@ -4872,7 +4878,7 @@ int sqlite3changegroup_add_strm( rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); if( rc==SQLITE_OK ){ - rc = sessionChangesetToHash(pIter, pGrp); + rc = sessionChangesetToHash(pIter, pGrp, 0); } sqlite3changeset_finalize(pIter); return rc; @@ -4957,4 +4963,256 @@ int sqlite3changeset_concat_strm( return rc; } +struct sqlite3_rebaser { + sqlite3_changegroup grp; /* Hash table */ +}; + +/* +** Buffers a1 and a2 must both contain a sessions module record nCol +** fields in size. This function appends an nCol sessions module +** record to buffer pBuf that is a copy of a1, except that: +** +** + If bUndefined is 0, for each field that is not "undefined" in either +** a1[] or a2[], swap in the field from a2[]. +** +** + If bUndefined is 1, for each field that is "undefined" in a1[] +** swap in the field from a2[]. +*/ +static void sessionAppendRecordMerge( + SessionBuffer *pBuf, + int nCol, + int bUndefined, + u8 *a1, int n1, + u8 *a2, int n2, + int *pRc +){ + sessionBufferGrow(pBuf, n1+n2, pRc); + if( *pRc==SQLITE_OK ){ + int i; + u8 *pOut = &pBuf->aBuf[pBuf->nBuf]; + for(i=0; izTab; + for(pTab=p->grp.pList; pTab; pTab=pTab->pNext){ + if( 0==sqlite3_stricmp(pTab->zName, zTab) ) break; + } + bNew = 0; + + /* Append a table header to the output for this new table */ + sessionAppendByte(&sOut, pIter->bPatchset ? 'P' : 'T', &rc); + sessionAppendVarint(&sOut, pIter->nCol, &rc); + sessionAppendBlob(&sOut, pIter->abPK, pIter->nCol, &rc); + sessionAppendBlob(&sOut, (u8*)pIter->zTab, strlen(pIter->zTab)+1, &rc); + } + + if( pTab ){ + int bPkOnly = (pIter->bPatchset && pIter->op==SQLITE_DELETE); + int iHash = sessionChangeHash(pTab, bPkOnly, aRec, pTab->nChange); + + for(pChange=pTab->apChange[iHash]; pChange; pChange=pChange->pNext){ + if( sessionChangeEqual(pTab, bPkOnly, aRec, 0, pChange->aRecord) ){ + break; + } + } + } + + if( pChange ){ + assert( pChange->op==SQLITE_DELETE || pChange->op==SQLITE_INSERT ); + /* If pChange is an INSERT, then rebase the change. If it is a + ** DELETE, omit the change from the output altogether. */ + if( pChange->op==SQLITE_INSERT ){ + if( pChange->bIndirect ){ + /* The change being rebased against was a DELETE. So, if the + ** input is a: + ** + ** DELETE - omit the change altogether. + ** UPDATE - change to an INSERT, + ** INSERT - no change (output the record as is). + */ + if( pIter->op!=SQLITE_DELETE ){ + sessionAppendByte(&sOut, SQLITE_INSERT, &rc); + sessionAppendByte(&sOut, pIter->bIndirect, &rc); + if( pIter->op==SQLITE_INSERT ){ + sessionAppendBlob(&sOut, aRec, nRec, &rc); + }else{ + sessionAppendRecordMerge(&sOut, pIter->nCol, 1, + aRec, nRec, pChange->aRecord, pChange->nRecord, &rc + ); + } + } + }else{ + sessionAppendByte(&sOut, pIter->op, &rc); + sessionAppendByte(&sOut, pIter->bIndirect, &rc); + if( pIter->op==SQLITE_INSERT ){ + sessionAppendBlob(&sOut, aRec, nRec, &rc); + }else{ + u8 *pCsr = aRec; + sessionAppendRecordMerge(&sOut, pIter->nCol, 0, + aRec, nRec, pChange->aRecord, pChange->nRecord, &rc + ); + if( pIter->op==SQLITE_UPDATE ){ + sessionSkipRecord(&pCsr, pIter->nCol); + sessionAppendBlob(&sOut, pCsr, nRec - (pCsr-aRec), &rc); + } + } + } + } + }else{ + sessionAppendByte(&sOut, pIter->op, &rc); + sessionAppendByte(&sOut, pIter->bIndirect, &rc); + sessionAppendBlob(&sOut, aRec, nRec, &rc); + } + + if( rc==SQLITE_OK && xOutput && sOut.nBuf>SESSIONS_STRM_CHUNK_SIZE ){ + rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); + sOut.nBuf = 0; + } + if( rc ) break; + } + + if( rc!=SQLITE_OK ){ + sqlite3_free(sOut.aBuf); + memset(&sOut, 0, sizeof(sOut)); + } + + if( rc==SQLITE_OK ){ + if( xOutput ){ + if( sOut.nBuf>0 ){ + rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); + } + }else{ + *ppOut = (void*)sOut.aBuf; + *pnOut = sOut.nBuf; + sOut.aBuf = 0; + } + } + sqlite3_free(sOut.aBuf); + return rc; +} + +/* +** Create a new rebaser object. +*/ +int sqlite3rebaser_create(sqlite3_rebaser **ppNew){ + int rc = SQLITE_OK; + sqlite3_rebaser *pNew; + + pNew = sqlite3_malloc(sizeof(sqlite3_rebaser)); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + } + *ppNew = pNew; + return rc; +} + +/* +** Call this one or more times to configure a rebaser. +*/ +int sqlite3rebaser_configure( + sqlite3_rebaser *p, + int nRebase, const void *pRebase +){ + sqlite3_changeset_iter *pIter = 0; /* Iterator opened on pData/nData */ + int rc; /* Return code */ + rc = sqlite3changeset_start(&pIter, nRebase, (void*)pRebase); + if( rc==SQLITE_OK ){ + rc = sessionChangesetToHash(pIter, &p->grp, 1); + } + sqlite3changeset_finalize(pIter); + return rc; +} + +/* +** Rebase a changeset according to current rebaser configuration +*/ +int sqlite3rebaser_rebase( + sqlite3_rebaser *p, + int nIn, const void *pIn, + int *pnOut, void **ppOut +){ + sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */ + int rc = sqlite3changeset_start(&pIter, nIn, (void*)pIn); + + if( rc==SQLITE_OK ){ + rc = sessionRebase(p, pIter, 0, 0, pnOut, ppOut); + sqlite3changeset_finalize(pIter); + } + + return rc; +} + +/* +** Rebase a changeset according to current rebaser configuration +*/ +int sqlite3rebaser_rebase_strm( + sqlite3_rebaser *p, + int (*xInput)(void *pIn, void *pData, int *pnData), + void *pIn, + int (*xOutput)(void *pOut, const void *pData, int nData), + void *pOut +){ + sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */ + int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); + + if( rc==SQLITE_OK ){ + rc = sessionRebase(p, pIter, xOutput, pOut, 0, 0); + sqlite3changeset_finalize(pIter); + } + + return rc; +} + +/* +** Destroy a rebaser object +*/ +void sqlite3rebaser_destroy(sqlite3_rebaser *p){ + if( p ){ + sessionDeleteTable(p->grp.pList); + sqlite3_free(p); + } +} + #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index c6a2332b8d..a7281bd4e7 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -1216,6 +1216,27 @@ int sqlite3changeset_apply_v2( #define SQLITE_CHANGESET_REPLACE 1 #define SQLITE_CHANGESET_ABORT 2 +typedef struct sqlite3_rebaser sqlite3_rebaser; + +/* Create a new rebaser object */ +int sqlite3rebaser_create(sqlite3_rebaser **ppNew); + +/* Call this one or more times to configure a rebaser */ +int sqlite3rebaser_configure( + sqlite3_rebaser*, + int nRebase, const void *pRebase +); + +/* Rebase a changeset according to current rebaser configuration */ +int sqlite3rebaser_rebase( + sqlite3_rebaser*, + int nIn, const void *pIn, + int *pnOut, void **ppOut +); + +/* Destroy a rebaser object */ +void sqlite3rebaser_destroy(sqlite3_rebaser *p); + /* ** CAPI3REF: Streaming Versions of API functions. ** @@ -1373,6 +1394,13 @@ int sqlite3changegroup_output_strm(sqlite3_changegroup*, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); +int sqlite3rebaser_rebase_strm( + sqlite3_rebaser *pRebaser, + int (*xInput)(void *pIn, void *pData, int *pnData), + void *pIn, + int (*xOutput)(void *pOut, const void *pData, int nData), + void *pOut +); /* diff --git a/ext/session/test_session.c b/ext/session/test_session.c index dd2d9977aa..7b08267b53 100644 --- a/ext/session/test_session.c +++ b/ext/session/test_session.c @@ -761,9 +761,18 @@ static int SQLITE_TCLAPI testSqlite3changesetApply( }else{ sStr.aData = (unsigned char*)pChangeset; sStr.nData = nChangeset; - rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr, - (objc==5) ? test_filter_handler : 0, test_conflict_handler, (void *)&ctx - ); + 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 + ); + } } if( rc!=SQLITE_OK ){ diff --git a/manifest b/manifest index 5e08ac632c..b887be3c0a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssqlite3_changeset_apply_v2()\sand\sapply_v2_strm()\sto\sthe\ssessions\smodule. -D 2018-03-13T20:31:23.940 +C Add\slargely\suntested\sAPIs\sfor\srebasing\schangesets. +D 2018-03-14T21:06:58.004 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 7016fc56c6b9bfe5daac4f34be8be38d8c0b5fab79ccbfb764d3b23bf1c6fff3 @@ -403,9 +403,9 @@ F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0 F ext/session/sessionrebase.test b4ac7545e3c69deaeab061c2bf36ad9e99aa6c38db94c340d7e48a230a9d4be8 F ext/session/sessionstat1.test 41cd97c2e48619a41cdf8ae749e1b25f34719de638689221aa43971be693bf4e F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc -F ext/session/sqlite3session.c 564e609f3086510d319e8abc0899fa79131d071b7dc01138e1b75bbf4a43cb03 -F ext/session/sqlite3session.h 8fe499b736633021094b814cf128691587f19c1943e372a23db37175bdeb8c67 -F ext/session/test_session.c 6c45bee58063f2c3d1edc213833d72f0a3ec651cbe27393b9e4a054e711b7c44 +F ext/session/sqlite3session.c 16561ad7eb8270bd3d2ee42c434043e68831bbd452fbea82d83922a1066a7cc8 +F ext/session/sqlite3session.h 74ba48151f3593a66a975ac095d7b53efa6c1e12fe83a903e10bf8d85a1429dd +F ext/session/test_session.c 8c04dc8cada82bd4e12f18ada3e35b56a8fd4d8dee7caac324ae28091c2b492f F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f @@ -1713,10 +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 0171d4a71ca7911a9fd409a42eeed0eda4521b6e48df5cd058364c0a736313b7 -R 1a6994480e1e2c2b73e68977f31547a3 -T *branch * sessions-rebase -T *sym-sessions-rebase * -T -sym-trunk * +P 445bfe977d9f3a891e08ef33237862ed047fe83e134ef3ed8b47ee0f5abd8cd6 +R 485a23056d6edbdb25721aa9069ac291 U dan -Z d8bbc56681a1d6ff1f4fb97dc44d1b66 +Z e2563dee69381fb6fb02a6ddaa823fec diff --git a/manifest.uuid b/manifest.uuid index 1e1f7573de..7b1553cdcd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -445bfe977d9f3a891e08ef33237862ed047fe83e134ef3ed8b47ee0f5abd8cd6 \ No newline at end of file +39915b683b3f8d3bf872af1dede96bf2818b488a8638a1d248395023fc4bd0ef \ No newline at end of file