diff options
author | Patrick Ohly <patrick.ohly@intel.com> | 2012-08-24 15:33:50 +0200 |
---|---|---|
committer | Patrick Ohly <patrick.ohly@intel.com> | 2012-08-29 11:44:30 +0200 |
commit | de7ff9fdc76a443ff87372ca1d91e22ce5e85236 (patch) | |
tree | d6b037d6a1e881486b49510ab0b4dcf0334a5a65 | |
parent | 72033969e09934c4630fba20585f8b47c85358e8 (diff) |
engine: override merge optionspbap
In caching mode, the local data is meant to be an exact copy of the
client's data. If the client removes fields in an item and then does a
slow sync, no fields of a matching server item are meant to be
preserved (= merged).
This can be achieved with merge=no in the field list. But the field
list is static and shared between data stores, which might not all
operate in caching mode. This makes updating the config cumbersome.
Therefore this patch adds a flag to the merging functions instead
which overrides the configured behavior such that fields are always
copied from one side into the other. This is activated when in caching
mode and also (optionally) via the MERGEITEMS() function.
The comparison is still needed, to determine whether anything changed
at all during a slow sync and prevent unnecessary database writes.
The loosing side is always the server in caching mode, no need to
check (similar to making the local store read-only).
This all applies to "slow sync" and "conflict" code paths.
In caching mode, a <mergescript> is currently ignored in favor of
doing the intended copying directly. Needs to be fixed.
-rw-r--r-- | src/sysync/localengineds.cpp | 20 | ||||
-rwxr-xr-x | src/sysync/multifielditem.cpp | 26 | ||||
-rwxr-xr-x | src/sysync/multifielditem.h | 4 | ||||
-rwxr-xr-x | src/sysync/multifielditemtype.cpp | 7 | ||||
-rwxr-xr-x | src/sysync/syncitem.h | 7 |
5 files changed, 45 insertions, 19 deletions
diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index f6c5be4..c81c412 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -5652,6 +5652,10 @@ bool TLocalEngineDS::engProcessRemoteItemAsServer( PDEBUGPRINTFX(DBG_DATA,("Read-Only or IgnoreUpdate: server always wins")); crstrategy=cr_server_wins; } + else if (fCacheData) { + PDEBUGPRINTFX(DBG_DATA,("Caching data: client always wins")); + crstrategy=cr_client_wins; + } else { // two-way crstrategy = fItemConflictStrategy; // get conflict strategy pre-set for this item @@ -5849,7 +5853,7 @@ bool TLocalEngineDS::engProcessRemoteItemAsServer( aStatusCommand.setStatusCode(208); // client wins fConflictsClientWins++; // - attempt to merge data from loosing item (accumulating fields) - if (!deleteconflict) { + if (!fCacheData && !deleteconflict) { aSyncItemP->mergeWith(*conflictingItemP,changedincoming,changedexisting,this); } if (changedincoming) { @@ -5977,11 +5981,19 @@ bool TLocalEngineDS::engProcessRemoteItemAsServer( } // if adds prevented, we cannot duplicate, let server win if (fPreventAdd && crstrategy==cr_duplicate) crstrategy=cr_server_wins; + else if (fCacheData) crstrategy=cr_client_wins; // now execute chosen strategy if (crstrategy==cr_client_wins) { - // - merge server's data into client item - PDEBUGPRINTFX(DBG_DATA,("Trying to merge some data from loosing server item into winning client item")); - aSyncItemP->mergeWith(*matchingItemP,changedincoming,changedexisting,this); + if (fCacheData) { + // - merge server's data into client item + PDEBUGPRINTFX(DBG_DATA,("Caching: copying winning client item into loosing server item")); + aSyncItemP->mergeWith(*matchingItemP,changedincoming,changedexisting,this, + TSyncItem::MERGE_OPTION_CHANGE_OTHER); + } else { + // - merge server's data into client item + PDEBUGPRINTFX(DBG_DATA,("Trying to merge some data from loosing server item into winning client item")); + aSyncItemP->mergeWith(*matchingItemP,changedincoming,changedexisting,this); + } // only count if server gets updated if (changedexisting) fConflictsClientWins++; // Note: changedexisting will cause needserverupdate to be set below diff --git a/src/sysync/multifielditem.cpp b/src/sysync/multifielditem.cpp index 3436c29..3d00435 100755 --- a/src/sysync/multifielditem.cpp +++ b/src/sysync/multifielditem.cpp @@ -1356,15 +1356,15 @@ bool TMultiFieldItem::checkItem(TLocalEngineDS *aDatastoreP) // - also updates other item to make sure it is equal to the winning after the merge // sets (but does not reset) change status of this and other item. // Note that changes of non-relevant fields are not reported here. -void TMultiFieldItem::mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS *aDatastoreP) +void TMultiFieldItem::mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS *aDatastoreP, int mode) { TMultiFieldItem *multifielditemP = castToSameTypeP(&aItem); if (!multifielditemP) return; // do the merge - if (fItemTypeP) + if (fItemTypeP && mode == MERGE_OPTION_FROM_CONFIG) fItemTypeP->mergeItems(*this,*multifielditemP,aChangedThis,aChangedOther,aDatastoreP); else - standardMergeWith(*multifielditemP,aChangedThis,aChangedOther); + standardMergeWith(*multifielditemP,aChangedThis,aChangedOther, mode); // show result OBJDEBUGPRINTFX(getItemType()->getSession(),DBG_DATA+DBG_CONFLICT,( "mergeWith() final status: thisitem: %schanged, otheritem: %schanged (relevant; eqm_none field changes are not indicated)", @@ -1380,7 +1380,8 @@ void TMultiFieldItem::mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aCha // - also updates other item to make sure it is equal to the winning after the merge // returns update status of this and other item. Note that changes of non-relevant fields are // not reported here. -void TMultiFieldItem::standardMergeWith(TMultiFieldItem &aItem, bool &aChangedThis, bool &aChangedOther) +void TMultiFieldItem::standardMergeWith(TMultiFieldItem &aItem, bool &aChangedThis, bool &aChangedOther, + int mode) { // same type of multifield, try to merge for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) { @@ -1401,7 +1402,7 @@ void TMultiFieldItem::standardMergeWith(TMultiFieldItem &aItem, bool &aChangedTh bool winning = winningField.isAssigned(); bool loosing = loosingField.isAssigned(); // - now decide what to do - if (sep!=mem_none) { + if (sep!=mem_none && mode == MERGE_OPTION_FROM_CONFIG) { // merge enabled PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT,( "Field '%s' available and enabled for merging, mode/sep=0x%04hX, %srelevant", @@ -1483,7 +1484,11 @@ void TMultiFieldItem::standardMergeWith(TMultiFieldItem &aItem, bool &aChangedTh // assignment just passes the proxy) if (!mergerelevant) { // everything is handled by the field assignment mechanisms - loosingField = winningField; + if (mode == MERGE_OPTION_CHANGE_THIS) { + winningField = loosingField; + } else { + loosingField = winningField; + } } else if (winningField!=loosingField) { // merge relevant fields will get more sophisticated treatment, such @@ -1498,16 +1503,17 @@ void TMultiFieldItem::standardMergeWith(TMultiFieldItem &aItem, bool &aChangedTh FMT_LENGTH_LIMITED(30,wfv.c_str()),FMT_LENGTH_LIMITED(30,lfv.c_str()) )); #endif - // update loosing item, too - if (loosingField.isShortVers(winningField,fItemTypeP->getFieldOptions(i)->maxsize)) { + // update loosing item, too, unless the winning field is shorter or explicitly requested + if (mode == MERGE_OPTION_CHANGE_THIS || + loosingField.isShortVers(winningField,fItemTypeP->getFieldOptions(i)->maxsize)) { // winning field is short version of loosing field -> loosing field is "better", use it winningField=loosingField; - if (mergerelevant) aChangedThis=true; + aChangedThis=true; } else { // standard case, loosing field is replaced by winning field loosingField=winningField; - if (mergerelevant) aChangedOther=true; + aChangedOther=true; } // this is some kind of item-level merge as well #ifdef SYDEBUG diff --git a/src/sysync/multifielditem.h b/src/sysync/multifielditem.h index 0582f66..0cc14bd 100755 --- a/src/sysync/multifielditem.h +++ b/src/sysync/multifielditem.h @@ -325,9 +325,9 @@ public: // - also updates other item to make sure it is equal to the winning after the merge // sets (but does not reset) change status of this and other item. // Note that changes of non-relevant fields are not reported here. - virtual void mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS *aDatastoreP); + virtual void mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS *aDatastoreP, int mode = MERGE_OPTION_FROM_CONFIG); // standard merge (subset of mergeWith, used if no merge script is defined) - void standardMergeWith(TMultiFieldItem &aItem, bool &aChangedThis, bool &aChangedOther); + void standardMergeWith(TMultiFieldItem &aItem, bool &aChangedThis, bool &aChangedOther, int mode = MERGE_OPTION_FROM_CONFIG); // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem // SYSYNC_NOT_COMPARABLE if not equal and no ordering known virtual sInt16 compareWith( diff --git a/src/sysync/multifielditemtype.cpp b/src/sysync/multifielditemtype.cpp index 02d629f..6dd32d9 100755 --- a/src/sysync/multifielditemtype.cpp +++ b/src/sysync/multifielditemtype.cpp @@ -189,12 +189,15 @@ public: }; // func_IgnoreUpdate - // void MERGEFIELDS() + // void MERGEFIELDS(mode = 0) + // Optional mode parameter determines the result of the merge. + // 0 = according to config, 1 = loosing item is overwritten, 2 = winning item is overwritten static void func_MergeFields(TItemField *&aTermP, TScriptContext *aFuncContextP) { TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext()); if (mfitP->fFirstItemP) - mfitP->fFirstItemP->standardMergeWith(*(mfitP->fSecondItemP),mfitP->fChangedFirst,mfitP->fChangedSecond); + mfitP->fFirstItemP->standardMergeWith(*(mfitP->fSecondItemP),mfitP->fChangedFirst,mfitP->fChangedSecond, + aFuncContextP->getLocalVar(0)->getAsInteger()); }; // func_MergeFields diff --git a/src/sysync/syncitem.h b/src/sysync/syncitem.h index df40c15..ae455c9 100755 --- a/src/sysync/syncitem.h +++ b/src/sysync/syncitem.h @@ -126,13 +126,18 @@ public: virtual bool replaceDataFrom(TSyncItem & /* aItem */, bool /* aAvailableOnly */=false, bool /* aDetectCutoffs */=false, bool /* aAssignedOnly */=false, bool /* aTransferUnassigned */=false) { return true; }; // no data -> nop // check item before processing it virtual bool checkItem(TLocalEngineDS * /* aDatastoreP */) { return true; }; // default is: ok + enum { + MERGE_OPTION_FROM_CONFIG, /**< merge as defined in the field list */ + MERGE_OPTION_CHANGE_OTHER, /**< ensure that the other item is the same as this item */ + MERGE_OPTION_CHANGE_THIS /**< ensure that this items is the same as the other */ + }; // merge this item with specified item. // Notes: // - specified item is treated as loosing item, this item is winning item // - also updates other item to make sure it is equal to the winning after the merge // sets (but does not reset) change status of this and other item. // Note that changes of non-relevant fields are not reported here. - virtual void mergeWith(TSyncItem & /* aItem */, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS * /* aDatastoreP */) { aChangedThis=false; aChangedOther=false; }; // nop by default + virtual void mergeWith(TSyncItem & /* aItem */, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS * /* aDatastoreP */, int mode = MERGE_OPTION_FROM_CONFIG) { aChangedThis=false; aChangedOther=false; }; // nop by default // remote and local ID string fRemoteID; // ID in remote party (if this is a server: LUID, GUID otherwise) string fLocalID; // ID in this party (if this is a server: GUID, LUID otherwise) |