Bug Summary

File:libsynthesis/src/sysync/customimplds.cpp
Warning:line 2932, column 21
Forming reference to null pointer

Annotated Source Code

1/**
2 * @File customimpl.cpp
3 *
4 * @Author Lukas Zeller (luz@plan44.ch)
5 *
6 * @brief TCustomImplDS
7 * Base class for customizable datastores (mainly extended DB mapping features
8 * common to all derived classes like ODBC, DBAPI etc.).
9 *
10 * Copyright (c) 2001-2011 by Synthesis AG + plan44.ch
11 *
12 * @Date 2005-12-05 : luz : separated from odbcapids
13 */
14
15
16// includes
17#include "sysync.h"
18#include "multifielditem.h"
19#include "mimediritemtype.h"
20#include "customimplds.h"
21#include "customimplagent.h"
22
23#ifdef DBAPI_TUNNEL_SUPPORT
24#include "SDK_util.h"
25#endif
26
27
28namespace sysync {
29
30#ifndef BINFILE_ALWAYS_ACTIVE
31#ifdef SYDEBUG2
32const char * const MapEntryTypeNames[numMapEntryTypes] = {
33 "invalid",
34 "normal",
35 "tempidmap",
36 "pendingmap"
37};
38#endif
39#endif // not BINFILE_ALWAYS_ACTIVE
40
41
42#ifdef SCRIPT_SUPPORT1
43
44class TCustomDSfuncs {
45public:
46
47 // Custom Impl datastore specific script functions
48 // ===============================================
49
50 // string FOLDERKEY()
51 // returns folder key
52 static void func_FolderKey(TItemField *&aTermP, TScriptContext *aFuncContextP)
53 {
54 aTermP->setAsString(
55 static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fFolderKey.c_str()
56 );
57 }; // func_FolderKey
58
59
60 // string TARGETKEY()
61 // returns target key
62 static void func_TargetKey(TItemField *&aTermP, TScriptContext *aFuncContextP)
63 {
64 aTermP->setAsString(
65 static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fTargetKey.c_str()
66 );
67 }; // func_TargetKey
68
69
70 // integer ARRAYINDEX()
71 // returns current array index when reading or writing an array
72 // in the finish function it denotes the number of array items totally
73 static void func_ArrayIndex(TItemField *&aTermP, TScriptContext *aFuncContextP)
74 {
75 aTermP->setAsInteger(
76 static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fArrIdx
77 );
78 }; // func_ArrayIndex
79
80
81 // string PARENTKEY()
82 // returns key of (array) parent object (like %k)
83 static void func_ParentKey(TItemField *&aTermP, TScriptContext *aFuncContextP)
84 {
85 aTermP->setAsString(
86 static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fParentKey.c_str()
87 );
88 }; // func_ParentKey
89
90
91 // integer WRITING()
92 // returns true if script is called while writing to DB
93 static void func_Writing(TItemField *&aTermP, TScriptContext *aFuncContextP)
94 {
95 aTermP->setAsBoolean(
96 static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fWriting
97 );
98 }; // func_Writing
99
100
101 // integer DELETING()
102 // returns true if script is called while deleting in DB
103 static void func_Deleting(TItemField *&aTermP, TScriptContext *aFuncContextP)
104 {
105 aTermP->setAsBoolean(
106 static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fDeleting
107 );
108 }; // func_Deleting
109
110
111 // integer INSERTING()
112 // returns true if script is called while inserting new data to DB
113 static void func_Inserting(TItemField *&aTermP, TScriptContext *aFuncContextP)
114 {
115 aTermP->setAsBoolean(
116 static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fInserting
117 );
118 }; // func_Inserting
119
120
121 // string LOGSUBST(string logtext)
122 // returns log placeholders substituted in logtext
123 static void func_LogSubst(TItemField *&aTermP, TScriptContext *aFuncContextP)
124 {
125 string logtext;
126 aFuncContextP->getLocalVar(0)->getAsString(logtext); // log text
127 // perform substitutions
128 static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->DoLogSubstitutions(logtext,false);
129 // return it
130 aTermP->setAsString(logtext);
131 }; // func_LogSubst
132
133}; // TCustomDSfuncs
134
135const uInt8 param_LogSubst[] = { VAL(fty_string)( (uInt8)fty_string) };
136
137// builtin function table for datastore level
138const TBuiltInFuncDef CustomDSFuncDefs[] = {
139 { "FOLDERKEY", TCustomDSfuncs::func_FolderKey, fty_string, 0, NULL__null },
140 { "TARGETKEY", TCustomDSfuncs::func_TargetKey, fty_string, 0, NULL__null },
141 { "ARRAYINDEX", TCustomDSfuncs::func_ArrayIndex, fty_integer, 0, NULL__null },
142 { "PARENTKEY", TCustomDSfuncs::func_ParentKey, fty_string, 0, NULL__null },
143 { "WRITING", TCustomDSfuncs::func_Writing, fty_integer, 0, NULL__null },
144 { "INSERTING", TCustomDSfuncs::func_Inserting, fty_integer, 0, NULL__null },
145 { "DELETING", TCustomDSfuncs::func_Deleting, fty_integer, 0, NULL__null },
146 { "LOGSUBST", TCustomDSfuncs::func_LogSubst, fty_string, 1, param_LogSubst }
147};
148
149
150// chain to generic local engine datastore funcs
151static void *CustomDSChainFunc2(void *&aCtx)
152{
153 // context pointer for datastore-level funcs is the datastore
154 // -> no change needed
155 // next table is localEngineDS's
156 return (void *)&DBFuncTable;
157} // CustomDSChainFunc2
158
159const TFuncTable CustomDSFuncTable2 = {
160 sizeof(CustomDSFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
161 CustomDSFuncDefs, // table pointer
162 CustomDSChainFunc2 // chain generic DB functions
163};
164
165
166
167#endif
168
169
170
171// Config
172// ======
173
174TCustomDSConfig::TCustomDSConfig(const char* aName, TConfigElement *aParentElement) :
175 inherited(aName,aParentElement),
176 fFieldMappings("mappings",this)
177 #ifdef SCRIPT_SUPPORT1
178 ,fResolveContextP(NULL__null)
179 ,fDSScriptsResolved(false)
180 #endif
181{
182 // nop so far
183 clear();
184} // TCustomDSConfig::TCustomDSConfig
185
186
187TCustomDSConfig::~TCustomDSConfig()
188{
189 // clear
190 clear();
191} // TCustomDSConfig::~TCustomDSConfig
192
193
194// init defaults
195void TCustomDSConfig::clear(void)
196{
197 // init defaults
198 // - multi-folder support
199 fMultiFolderDB=false;
200 // Data table options
201 // - charset to be used in the data table
202 fDataCharSet=chs_ansi; // suitable default for ODBC
203 // - line end mode to be used in the data table for multiline data
204 fDataLineEndMode=lem_dos; // default to CRLF, as this seems to be safest assumption
205 // - if set, causes that data is read from DB first and then merged
206 // with updated fields. Not needed in normal SQL DBs, as they can
207 // update a subset of all columns. However, might still be needed
208 // for special cases like Achil that needs record size calculation
209 // or sortfeldXXX generation.
210 fUpdateAllFields=false;
211 // - Date/Time info
212 fDataIsUTC=false; // compatibility flag only, will set fDataTimeZone to TCTX_UTC at Resolve if set
213 fDataTimeZone=TCTX_SYSTEM((timecontext_t) ((tctx_tz_system) | TCTX_SYMBOLIC_TZ)); // default to local system time
214 fUserZoneOutput=true; // by default, non-floating timestamps are moved to user zone after reading from DB. Only if zone context for timestamp fields is really retrieved from the DB on a per record level, this can be switched off
215 #ifndef BINFILE_ALWAYS_ACTIVE
216 // - flag indicating that admin tables have DS 1.2 support (map entrytype, map flags, fResumeAlertCode, fLastSuspend, fLastSuspendIdentifier
217 fResumeSupport=false;
218 fResumeItemSupport=false; // no item resume as well
219 // - admin capability info
220 fSyncTimeStampAtEnd=false; // if set, time point of sync is taken AFTER last write to DB (for single-user DBs like FMPro). Note that target table layout is different in this case!
221 fOneWayFromRemoteSupported=false; // compatible with old layout of target tables, no support
222 #endif // not BINFILE_ALWAYS_ACTIVE
223 fStoreSyncIdentifiers=false; // compatible with old layout of target tables, no support
224 // clear embedded
225 fFieldMappings.clear();
226 #ifdef SCRIPT_SUPPORT1
227 // - script called after admin data is loaded (before any data access takes place)
228 fAdminReadyScript.erase();
229 // - script called at end of sync session
230 fSyncEndScript.erase();
231 // - clear script resolve context
232 if (fResolveContextP) {
233 delete fResolveContextP;
234 fResolveContextP=NULL__null;
235 }
236 fDSScriptsResolved=false;
237 #endif
238 // clear inherited
239 inherited::clear();
240} // TCustomDSConfig::clear
241
242
243// config element parsing
244bool TCustomDSConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
245{
246 // multi-folder-support
247 if (strucmp(aElementName,"multifolder")==0)
248 expectBool(fMultiFolderDB);
249 // user data related properties
250 else if (strucmp(aElementName,"datacharset")==0)
251 expectEnum(sizeof(fDataCharSet),&fDataCharSet,DBCharSetNames,numCharSets);
252 else if (strucmp(aElementName,"datalineends")==0)
253 expectEnum(sizeof(fDataLineEndMode),&fDataLineEndMode,lineEndModeNames,numLineEndModes);
254 else if (strucmp(aElementName,"updateallfields")==0)
255 expectBool(fUpdateAllFields);
256 // - Date/Time info
257 else if (strucmp(aElementName,"timeutc")==0) {
258 // - warn for usage of old timeutc
259 ReportError(false,"Warning: <timeutc> is deprecated - please use <datatimezone> instead",aElementName);
260 expectBool(fDataIsUTC);
261 }
262 else if (strucmp(aElementName,"datatimezone")==0)
263 expectTimezone(fDataTimeZone);
264 else if (strucmp(aElementName,"userzoneoutput")==0)
265 expectBool(fUserZoneOutput);
266 #ifndef BINFILE_ALWAYS_ACTIVE
267 // - admin capability info
268 else if (strucmp(aElementName,"synctimestampatend")==0)
269 expectBool(fSyncTimeStampAtEnd);
270 else if (strucmp(aElementName,"fromremoteonlysupport")==0)
271 expectBool(fOneWayFromRemoteSupported);
272 else if (strucmp(aElementName,"resumesupport")==0)
273 expectBool(fResumeSupport);
274 else if (strucmp(aElementName,"resumeitemsupport")==0)
275 expectBool(fResumeItemSupport);
276 #endif // BINFILE_ALWAYS_ACTIVE
277 else if (
278 strucmp(aElementName,"storelastsyncidentifier")==0 ||
279 strucmp(aElementName,"storesyncidentifiers")==0
280 )
281 expectBool(fStoreSyncIdentifiers);
282 #ifdef SCRIPT_SUPPORT1
283 else if (strucmp(aElementName,"adminreadyscript")==0)
284 expectScript(fAdminReadyScript, aLine, getDSFuncTableP());
285 else if (strucmp(aElementName,"syncendscript")==0)
286 expectScript(fSyncEndScript, aLine, getDSFuncTableP());
287 #endif
288 // - field mappings
289 else if (strucmp(aElementName,"fieldmap")==0) {
290 // check reference argument
291 const char* ref = getAttr(aAttributes,"fieldlist");
292 if (!ref) {
293 ReportError(true,"fieldmap missing 'fieldlist' attribute");
294 }
295 else {
296 // look for field list
297 TMultiFieldDatatypesConfig *mfcfgP =
298 DYN_CASTdynamic_cast<TMultiFieldDatatypesConfig *>(getSyncAppBase()->getRootConfig()->fDatatypesConfigP);
299 if (!mfcfgP) SYSYNC_THROW(TConfigParseException("no multifield config"))throw TConfigParseException("no multifield config");
300 TFieldListConfig *cfgP = mfcfgP->getFieldList(ref);
301 if (!cfgP)
302 return fail("fieldlist '%s' not defined for fieldmap",ref);
303 // - store field list reference in map
304 fFieldMappings.fFieldListP=cfgP;
305 // - let element handle parsing
306 expectChildParsing(fFieldMappings);
307 }
308 }
309 // - none known here
310 else
311 return inherited::localStartElement(aElementName,aAttributes,aLine);
312 // ok
313 return true;
314} // TCustomDSConfig::localStartElement
315
316
317// resolve
318void TCustomDSConfig::localResolve(bool aLastPass)
319{
320 // convert legacy UTC flag to timezone setting
321 if (fDataIsUTC)
322 fDataTimeZone = TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ));
323 // scripts
324 #ifdef SCRIPT_SUPPORT1
325 if (aLastPass) {
326 // resolve map scripts now in case they haven't been resolved already
327 ResolveDSScripts();
328 }
329 #endif
330 // resolve children
331 fFieldMappings.Resolve(aLastPass);
332 #ifdef SCRIPT_SUPPORT1
333 // Now everything is resolved, we can forget the resolve context
334 if (aLastPass && fResolveContextP) {
335 delete fResolveContextP;
336 fResolveContextP = NULL__null;
337 }
338 #endif
339 // resolve inherited
340 inherited::localResolve(aLastPass);
341} // TCustomDSConfig::localResolve
342
343
344#ifdef SCRIPT_SUPPORT1
345
346// resolve DS related scripts, but make sure we do that only once
347// Note: MAKE SURE that order of resolving is same as rebuilding order!
348void TCustomDSConfig::ResolveDSScripts(void)
349{
350 // resolve
351 if (!fDSScriptsResolved) {
352 // resolve possible API level scripts first
353 apiResolveScripts();
354 // resolve start and end scripts first
355 TScriptContext::resolveScript(getSyncAppBase(),fAdminReadyScript,fResolveContextP,NULL__null);
356 TScriptContext::resolveScript(getSyncAppBase(),fSyncEndScript,fResolveContextP,NULL__null);
357 // - option filter generator script
358 TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fOptionFilterScript,fResolveContextP,fFieldMappings.fFieldListP);
359 // - map scripts
360 TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fInitScript,fResolveContextP,fFieldMappings.fFieldListP);
361 TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fAfterReadScript,fResolveContextP,fFieldMappings.fFieldListP);
362 TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fBeforeWriteScript,fResolveContextP,fFieldMappings.fFieldListP);
363 TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fAfterWriteScript,fResolveContextP,fFieldMappings.fFieldListP);
364 TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fFinishScript,fResolveContextP,fFieldMappings.fFieldListP);
365 TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fFinalisationScript,fResolveContextP,fFieldMappings.fFieldListP);
366 fDSScriptsResolved=true;
367 }
368} // TCustomDSConfig::ResolveDSScripts
369
370#endif
371
372
373// transfer size limits from map to type
374static void transferMapOptionsToType(TFieldMapList &aFml, TMultiFieldItemType *aItemTypeP, sInt16 aMaxRepeat, sInt16 aRepInc)
375{
376 TFieldMapList::iterator pos;
377 TFieldMapItem *fmiP;
378
379 for (pos=aFml.begin(); pos!=aFml.end(); pos++) {
380 fmiP = *pos;
381 #ifdef ARRAYDBTABLES_SUPPORT1
382 if (fmiP->isArray()) {
383 // is array, recurse into fields of array
384 TFieldMapArrayItem *fmaiP = static_cast<TFieldMapArrayItem *>(fmiP);
385 transferMapOptionsToType(
386 fmaiP->fArrayFieldMapList,
387 aItemTypeP,
388 fmaiP->fMaxRepeat,
389 fmaiP->fRepeatInc
390 );
391 }
392 else
393 #endif
394 if (fmiP->fid>=0) {
395 // normal map, apply to all related fields
396 if (aMaxRepeat==0) aMaxRepeat=1; // unlimited repeat is only applied to first field
397 #ifdef ARRAYFIELD_SUPPORT1
398 TFieldDefinition *fdP=aItemTypeP->getFieldDefinition(fmiP->fid);
399 if (fdP && fdP->array) {
400 // this is an array field, disable fid offsetting
401 aMaxRepeat=1;
402 }
403 #endif
404 for (sInt16 k=0; k<aMaxRepeat; k++) {
405 TFieldOptions *optP = aItemTypeP->getFieldOptions(fmiP->fid+k*aRepInc);
406 if (!optP) break;
407 if (
408 optP->maxsize==FIELD_OPT_MAXSIZE_NONE0 || // no size defined yet
409 optP->maxsize==FIELD_OPT_MAXSIZE_UNKNOWN-1 || // or defined as unknown
410 (optP->maxsize>sInt32(fmiP->maxsize) && // or defined size is larger than that one set in the mapping...
411 fmiP->maxsize!=0) // ..but map size is not unlimited
412 ) {
413 // set new max size
414 optP->maxsize = fmiP->maxsize;
415 }
416 if (fmiP->notruncate)
417 optP->notruncate=true;
418 }
419 }
420 }
421} // transferMapOptionsToType
422
423
424// Add (probably datastore-specific) limits such as MaxSize and NoTruncate to types
425void TCustomDSConfig::addTypeLimits(TLocalEngineDS *aLocalDatastoreP, TSyncSession *aSessionP)
426{
427 // add field size limitations from map to all types
428 TSyncItemTypePContainer::iterator pos;
429 TSyncItemTypePContainer *typesP = &(aLocalDatastoreP->fRxItemTypes);
430 for (uInt8 i=0; i<2; i++) {
431 for (pos=typesP->begin(); pos!=typesP->end(); pos++) {
432 // apply maps to type
433 transferMapOptionsToType(
434 fFieldMappings.fFieldMapList,
435 static_cast<TMultiFieldItemType *>(*pos),
436 1,1 // single instance only
437 );
438 }
439 typesP = &(aLocalDatastoreP->fTxItemTypes);
440 }
441} // TCustomDSConfig::addTypeLimits
442
443
444// proptotype to make compiler happy
445bool parseMap(TCustomDSConfig *aCustomDSConfig, TConfigElement *cfgP, bool aIsArray, TFieldListConfig *aFieldListP, TFieldMapList &aFieldMapList, const char **aAttributes, bool aUpdateParams);
446
447// parse map items
448bool parseMap(TCustomDSConfig *aCustomDSConfig, TConfigElement *cfgP, bool aIsArray, TFieldListConfig *aFieldListP, TFieldMapList &aFieldMapList, const char **aAttributes, bool aUpdateParams)
449{
450 TFieldMapItem *mapitemP = NULL__null;
451 sInt16 fid = VARIDX_UNDEFINED-128;
452
453 // get name
454 const char* nam = cfgP->getAttr(aAttributes,"name");
455 // get base field reference is possible for arrays too, to specify the relevant array size
456 const char* ref = cfgP->getAttr(aAttributes,aIsArray ? "sizefrom" : "references");
457 if (ref) {
458 // get fid for referenced field
459 if (!aCustomDSConfig->fFieldMappings.fFieldListP) SYSYNC_THROW(TConfigParseException("map with no field list defined"))throw TConfigParseException("map with no field list defined");
460 #ifdef SCRIPT_SUPPORT1
461 fid = TConfigElement::getFieldIndex(ref,aCustomDSConfig->fFieldMappings.fFieldListP,aCustomDSConfig->fResolveContextP);
462 #else
463 fid = TConfigElement::getFieldIndex(ref,aCustomDSConfig->fFieldMappings.fFieldListP);
464 #endif
465 }
466 // now decide what to do
467 if (aUpdateParams) {
468 // only updating params of existing map (non-arrays only!)
469 // - search for existing map item by name
470 TFieldMapList::iterator pos;
471 for (pos=aFieldMapList.begin(); pos!=aFieldMapList.end(); pos++) {
472 // check for name
473 TFieldMapItem *fmiP = static_cast<TFieldMapItem *>(*pos);
474 if (strucmp(nam,fmiP->getName())==0) {
475 // found it
476 mapitemP = fmiP;
477 }
478 }
479 if (!mapitemP) {
480 return cfgP->fail("mapredefine must refer to an existing map");
481 }
482 }
483 else {
484 // creating a new map
485 #ifdef ARRAYDBTABLES_SUPPORT1
486 if (aIsArray) {
487 // array container
488 mapitemP = aCustomDSConfig->newFieldMapArrayItem(aCustomDSConfig,cfgP);
489 // save size field reference if any
490 mapitemP->fid=fid;
491 // extra attributes for derived classes
492 mapitemP->checkAttrs(aAttributes);
493 // let array container parse details
494 cfgP->expectChildParsing(*mapitemP);
495 }
496 #endif
497 }
498 if (!aIsArray) {
499 // simple map
500 cfgP->expectEmpty(); // plain maps may not have content
501 // process creation of map item
502 const char* type = cfgP->getAttr(aAttributes,"type");
503 const char* mode = cfgP->getAttr(aAttributes,"mode");
504 bool truncate = true;
505 cfgP->getAttrBool(aAttributes,"truncate",truncate,true);
506 if (!nam || !ref || !type)
507 return cfgP->fail("map must have 'name', 'references', 'type' and 'mode' attributes at least");
508 if (fid==VARIDX_UNDEFINED-128)
509 return cfgP->fail("map references unknown field '%s'",ref);
510 // convert type
511 sInt16 ty;
512 if (!StrToEnum(DBFieldTypeNames,numDBfieldTypes,ty,type))
513 return cfgP->fail("unknown type '%s'",type);
514 // convert mode
515 // - needed flags
516 bool rd,wr,fins,fupd;
517 // - optional flags
518 bool asparam=false,floatingts=false,needsfinalisation=false;
519 if (mode) {
520 rd=false,wr=false; fins=false; fupd=false;
521 while (*mode) {
522 if (tolower(*mode)=='r') rd=true;
523 else if (tolower(*mode)=='w') { wr=true; fins=true; fupd=true; } // for both insert and update
524 else if (tolower(*mode)=='i') { wr=true; fins=true; } // insert only
525 else if (tolower(*mode)=='u') { wr=true; fupd=true; } // update only
526 else if (tolower(*mode)=='p') { asparam=true; } // map as parameter (e.g. ODBC parameter mechanism for INSERT/UPDATE statements)
527 else if (tolower(*mode)=='f') { floatingts=true; } // map as floating time field (will be written as-is, no conversion from/to DB time zone takes place)
528 else if (tolower(*mode)=='x') { needsfinalisation=true; } // needs to be kept for finalisation at end of session (for relational link updates etc.)
529 else
530 return cfgP->fail("invalid mode '%c'",*mode);
531 // next char
532 mode++;
533 }
534 }
535 else {
536 // default mode for needed flags
537 rd=true; wr=true; fins=true; fupd=true;
538 }
539 // Optionals
540 // - size
541 sInt32 sz=0; // default to unlimited
542 if (!cfgP->getAttrLong(aAttributes,"size",sz,true))
543 cfgP->fail("invalid size specification");
544 // - statement index
545 sInt16 setno=0; // default to 0
546 if (!cfgP->getAttrShort(aAttributes,"set_no",setno,true))
547 cfgP->fail("invalid set_no specification");
548 // create mapitem, name is DB field name
549 if (!aUpdateParams) {
550 mapitemP = aCustomDSConfig->newFieldMapItem(nam,cfgP);
551 }
552 mapitemP->fid=fid;
553 mapitemP->dbfieldtype=(TDBFieldType)ty;
554 mapitemP->readable=rd;
555 mapitemP->writable=wr;
556 mapitemP->for_insert=fins;
557 mapitemP->for_update=fupd;
558 mapitemP->as_param=asparam;
559 mapitemP->floating_ts=floatingts;
560 mapitemP->needs_finalisation=needsfinalisation;
561 mapitemP->maxsize=(uInt32)sz;
562 mapitemP->notruncate=!truncate;
563 mapitemP->setNo=(uInt16)setno;
564 // extra attributes for derived classes
565 mapitemP->checkAttrs(aAttributes);
566 } // if normal map
567 if (!aUpdateParams) {
568 // - and add it to the list
569 aFieldMapList.push_back(mapitemP);
570 }
571 return true;
572} // parseMap
573
574
575// Field Map item
576// ==============
577
578TFieldMapItem::TFieldMapItem(const char *aElementName, TConfigElement *aParentElement) :
579 TConfigElement(aElementName,aParentElement)
580{
581 // default suitable for array container
582 readable=false;
583 writable=false;
584 for_insert=false;
585 for_update=false;
586 as_param=false;
587 floating_ts=false;
588 needs_finalisation=false;
589 setNo=0; // default set is 0
590 maxsize=0; // no max size
591 notruncate=false; // allow truncation by default
592 fid=VARIDX_UNDEFINED-128; // no field
593 dbfieldtype=dbft_string; // default to string
594} // TFieldMapItem::TFieldMapItem
595
596
597
598#ifdef ARRAYDBTABLES_SUPPORT1
599
600// array container
601// ===============
602
603TFieldMapArrayItem::TFieldMapArrayItem(TCustomDSConfig *aCustomDSConfigP, TConfigElement *aParentElement) :
604 TFieldMapItem("array",aParentElement),
605 fCustomDSConfigP(aCustomDSConfigP)
606{
607 clear();
608} // TFieldMapArrayItem::TFieldMapArrayItem
609
610
611TFieldMapArrayItem::~TFieldMapArrayItem()
612{
613 // nop so far
614 clear();
615} // TFieldMapArrayItem::~TFieldMapArrayItem
616
617
618// init defaults
619void TFieldMapArrayItem::clear(void)
620{
621 // init defaults
622 // - clear values
623 #ifdef OBJECT_FILTERING1
624 fNoItemsFilter.erase();
625 #endif
626 fMaxRepeat=1;
627 fRepeatInc=1;
628 fStoreEmpty=false;
629 #ifdef SCRIPT_SUPPORT1
630 // - clear scripts
631 fInitScript.erase();
632 fAfterReadScript.erase();
633 fBeforeWriteScript.erase();
634 fAfterWriteScript.erase();
635 fFinishScript.erase();
636 fScriptsResolved=false;
637 #endif
638 // - clear map items
639 TFieldMapList::iterator pos;
640 for (pos=fArrayFieldMapList.begin(); pos!=fArrayFieldMapList.end(); pos++)
641 delete (*pos);
642 fArrayFieldMapList.clear();
643 // clear inherited
644 inherited::clear();
645} // TFieldMapArrayItem::clear
646
647
648
649#ifdef SCRIPT_SUPPORT1
650
651void TFieldMapArrayItem::expectScriptUnresolved(string &aTScript,sInt32 aLine, const TFuncTable *aContextFuncs)
652{
653 if (fScriptsResolved) {
654 fail("array scripts must be defined before first <map> within array");
655 }
656 else {
657 expectScript(aTScript,aLine,aContextFuncs);
658 }
659} // TFieldMapArrayItem::expectScriptUnresolved
660
661#endif
662
663
664
665// config element parsing
666bool TFieldMapArrayItem::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
667{
668 // checking the elements
669 if (strucmp(aElementName,"map")==0) {
670 #ifdef SCRIPT_SUPPORT1
671 // early resolve basic map scripts so map entries can refer to local vars
672 if (!fScriptsResolved) ResolveArrayScripts();
673 #endif
674 // now parse new map item
675 return parseMap(fCustomDSConfigP,this,false,fCustomDSConfigP->fFieldMappings.fFieldListP,fArrayFieldMapList,aAttributes, false);
676 }
677 /* nested arrays not yet supported
678 // %%%% Note: if we do support them, we need to update
679 // the script resolution stuff above and make ProcessArrayScripts recursive
680 else if (strucmp(aElementName,"array")==0)
681 #ifdef SCRIPT_SUPPORT
682 // early resolve basic map scripts so map entries can refer to local vars
683 if (!fScriptsResolved) ResolveArrayScripts();
684 #endif
685 // now parse nested array map
686 return parseMap(fBaseFieldMappings,this,true,fFieldListP,fArrayFieldMapList,aAttributes, false);
687 */
688 else if (strucmp(aElementName,"maxrepeat")==0)
689 expectInt16(fMaxRepeat);
690 else if (strucmp(aElementName,"repeatinc")==0)
691 expectInt16(fRepeatInc);
692 else if (strucmp(aElementName,"storeempty")==0)
693 expectBool(fStoreEmpty);
694 #ifdef OBJECT_FILTERING1
695 else if (strucmp(aElementName,"noitemsfilter")==0)
696 expectString(fNoItemsFilter);
697 #endif
698 #ifdef SCRIPT_SUPPORT1
699 else if (strucmp(aElementName,"initscript")==0)
700 expectScriptUnresolved(fInitScript, aLine, fCustomDSConfigP->fFieldMappings.getDSFuncTableP());
701 else if (strucmp(aElementName,"afterreadscript")==0)
702 expectScriptUnresolved(fAfterReadScript, aLine, fCustomDSConfigP->fFieldMappings.getDSFuncTableP());
703 else if (strucmp(aElementName,"beforewritescript")==0)
704 expectScriptUnresolved(fBeforeWriteScript, aLine, fCustomDSConfigP->fFieldMappings.getDSFuncTableP());
705 else if (strucmp(aElementName,"afterwritescript")==0)
706 expectScriptUnresolved(fAfterWriteScript, aLine, fCustomDSConfigP->fFieldMappings.getDSFuncTableP());
707 else if (strucmp(aElementName,"finishscript")==0)
708 expectScriptUnresolved(fFinishScript, aLine, fCustomDSConfigP->fFieldMappings.getDSFuncTableP());
709 #endif
710 // - none known here
711 else
712 return inherited::localStartElement(aElementName,aAttributes,aLine);
713 // ok
714 return true;
715} // TFieldMapArrayItem::localStartElement
716
717
718
719#ifdef SCRIPT_SUPPORT1
720
721// process single array's scripts (resolve or rebuild them)
722void TFieldMapArrayItem::ResolveArrayScripts(void)
723{
724 // resolve
725 TScriptContext::resolveScript(getSyncAppBase(),fInitScript,fCustomDSConfigP->fResolveContextP,fCustomDSConfigP->fFieldMappings.fFieldListP);
726 TScriptContext::resolveScript(getSyncAppBase(),fAfterReadScript,fCustomDSConfigP->fResolveContextP,fCustomDSConfigP->fFieldMappings.fFieldListP);
727 TScriptContext::resolveScript(getSyncAppBase(),fBeforeWriteScript,fCustomDSConfigP->fResolveContextP,fCustomDSConfigP->fFieldMappings.fFieldListP);
728 TScriptContext::resolveScript(getSyncAppBase(),fAfterWriteScript,fCustomDSConfigP->fResolveContextP,fCustomDSConfigP->fFieldMappings.fFieldListP);
729 TScriptContext::resolveScript(getSyncAppBase(),fFinishScript,fCustomDSConfigP->fResolveContextP,fCustomDSConfigP->fFieldMappings.fFieldListP);
730 fScriptsResolved=true;
731} // TFieldMapArrayItem::ResolveArrayScripts
732
733#endif
734
735
736// resolve
737void TFieldMapArrayItem::localResolve(bool aLastPass)
738{
739 if (aLastPass) {
740 #ifdef SCRIPT_SUPPORT1
741 // resolve map scripts now in case they haven't been resolved already
742 if (!fScriptsResolved) ResolveArrayScripts();
743 #endif
744 }
745 // resolve inherited
746 inherited::localResolve(aLastPass);
747} // TFieldMapArrayItem::localResolve
748
749#endif
750
751
752// field mappings
753// ==============
754
755
756TFieldMappings::TFieldMappings(const char* aName, TConfigElement *aParentElement) :
757 TConfigElement(aName,aParentElement),
758 fFieldListP(NULL__null)
759{
760 clear();
761} // TFieldMappings::TFieldMappings
762
763
764TFieldMappings::~TFieldMappings()
765{
766 // nop so far
767 clear();
768} // TFieldMappings::~TFieldMappings
769
770
771// init defaults
772void TFieldMappings::clear(void)
773{
774 // init defaults
775 // - clear map items
776 TFieldMapList::iterator pos;
777 for (pos=fFieldMapList.begin(); pos!=fFieldMapList.end(); pos++)
778 delete (*pos);
779 fFieldMapList.clear();
780 #ifdef SCRIPT_SUPPORT1
781 // - clear scripts
782 fOptionFilterScript.erase();
783 fInitScript.erase();
784 fAfterReadScript.erase();
785 fBeforeWriteScript.erase();
786 fAfterWriteScript.erase();
787 fFinishScript.erase(); // fFinishScript is now in use for exit call of datastore handling
788 fFinalisationScript.erase(); // called for each item with fields having the needs_finalisation set (BEFORE fFinishScript)
789 #endif
790 // - clear reference
791 fFieldListP=NULL__null;
792 // clear inherited
793 inherited::clear();
794} // TFieldMappings::clear
795
796
797#ifdef SCRIPT_SUPPORT1
798
799void TFieldMappings::expectScriptUnresolved(string &aTScript,sInt32 aLine, const TFuncTable *aContextFuncs)
800{
801 TCustomDSConfig *dscfgP = static_cast<TCustomDSConfig *>(getParentElement());
802 if (dscfgP->fDSScriptsResolved) {
803 fail("database scripts must be defined before first <map>");
804 }
805 else {
806 expectScript(aTScript,aLine,aContextFuncs);
807 }
808} // TFieldMappings::expectScriptUnresolved
809
810#endif
811
812
813// config element parsing
814bool TFieldMappings::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
815{
816 TCustomDSConfig *dscfgP = static_cast<TCustomDSConfig *>(getParentElement());
817
818 // checking the elements
819 if (strucmp(aElementName,"map")==0) {
820 #ifdef SCRIPT_SUPPORT1
821 // early resolve basic datastore scripts so map entries can refer to local vars already
822 dscfgP->ResolveDSScripts();
823 #endif
824 // now parse map
825 return parseMap(dscfgP,this,false,fFieldListP,fFieldMapList,aAttributes, false);
826 }
827 else if (strucmp(aElementName,"mapredefine")==0) {
828 // allow specifying parameters for some maps in a automap generated mappings list
829 return parseMap(dscfgP,this,false,fFieldListP,fFieldMapList,aAttributes, true);
830 }
831 else if (strucmp(aElementName,"automap")==0) {
832 // auto-create map entries for all fields in the field list
833 // (this is a convenience function to allow nearly identical usage of textdb and dbapi with text module)
834 if (!fFieldListP) SYSYNC_THROW(TConfigParseException("automap with no field list defined"))throw TConfigParseException("automap with no field list defined"
)
;
835 // check mode option
836 bool indexAsName=false;
837 if (!getAttrBool(aAttributes,"indexasname",indexAsName,true))
838 fail("invalid indexasname value");
839 // iterate through field definitions and create a string mapping
840 TFieldDefinitionList::iterator pos;
841 sInt16 fid=0;
842 string fieldname;
843 for (pos=fFieldListP->fFields.begin(); pos!=fFieldListP->fFields.end(); ++pos, ++fid) {
844 // create DB field name
845 if (indexAsName)
846 StringObjPrintf(fieldname,"%hd",fid); // use fid as field name
847 else
848 fieldname=TCFG_CSTR(pos->fieldname)pos->fieldname.c_str(); // use internal field's name
849 // create mapitem using field index/itemfield name as DB field name
850 TFieldMapItem *mapitemP = static_cast<TCustomDSConfig *>(getParentElement())->newFieldMapItem(fieldname.c_str(),this);
851 mapitemP->fid=fid; // fid corresponds with position in field definitions list
852 mapitemP->dbfieldtype = pos->type==fty_timestamp ? dbft_timestamp : dbft_string; // map timestamps as such, otherwise all are strings
853 mapitemP->readable=true;
854 mapitemP->writable=true;
855 mapitemP->for_insert=true;
856 mapitemP->for_update=true;
857 // - and add it to the list
858 fFieldMapList.push_back(mapitemP);
859 }
860 // that's it
861 expectEmpty();
862 }
863 #ifdef ARRAYDBTABLES_SUPPORT1
864 else if (strucmp(aElementName,"array")==0) {
865 #ifdef SCRIPT_SUPPORT1
866 // early resolve basic datastore scripts so array map entries can refer to local vars already!
867 dscfgP->ResolveDSScripts();
868 #endif
869 // now parse array map
870 return parseMap(dscfgP,this,true,fFieldListP,fFieldMapList,aAttributes,false);
871 }
872 #endif
873 #ifdef SCRIPT_SUPPORT1
874 else if (strucmp(aElementName,"optionfilterscript")==0)
875 expectScriptUnresolved(fOptionFilterScript, aLine, getDSFuncTableP());
876 else if (strucmp(aElementName,"initscript")==0)
877 expectScriptUnresolved(fInitScript, aLine, getDSFuncTableP());
878 else if (strucmp(aElementName,"afterreadscript")==0)
879 expectScriptUnresolved(fAfterReadScript, aLine, getDSFuncTableP());
880 else if (strucmp(aElementName,"beforewritescript")==0)
881 expectScriptUnresolved(fBeforeWriteScript, aLine, getDSFuncTableP());
882 else if (strucmp(aElementName,"afterwritescript")==0)
883 expectScriptUnresolved(fAfterWriteScript, aLine, getDSFuncTableP());
884 else if (strucmp(aElementName,"finishscript")==0)
885 expectScriptUnresolved(fFinishScript, aLine, getDSFuncTableP());
886 else if (strucmp(aElementName,"finalisationscript")==0)
887 expectScriptUnresolved(fFinalisationScript, aLine, getDSFuncTableP());
888 #endif
889 // - none known here
890 else
891 return inherited::localStartElement(aElementName,aAttributes,aLine);
892 // ok
893 return true;
894} // TFieldMappings::localStartElement
895
896
897
898// resolve
899void TFieldMappings::localResolve(bool aLastPass)
900{
901 // resolve each map
902 TFieldMapList::iterator pos;
903 for (pos=fFieldMapList.begin(); pos!=fFieldMapList.end(); pos++) {
904 (*pos)->Resolve(aLastPass);
905 }
906 // resolve inherited
907 inherited::localResolve(aLastPass);
908} // TFieldMappings::localResolve
909
910
911#ifdef SCRIPT_SUPPORT1
912
913// access to DS func table pointer
914const TFuncTable *TFieldMappings::getDSFuncTableP(void)
915{
916 return static_cast<TCustomDSConfig *>(getParentElement())->getDSFuncTableP();
917} // TFieldMappings::getDSFuncTableP
918
919#endif
920
921/*
922 * Implementation of TCustomImplDS
923 */
924
925
926// constructor
927TCustomImplDS::TCustomImplDS(
928 TCustomDSConfig *aConfigP,
929 sysync::TSyncSession *aSessionP,
930 const char *aName,
931 uInt32 aCommonSyncCapMask
932) :
933 inherited(aConfigP,aSessionP, aName, aCommonSyncCapMask)
934{
935 fNeedFinalisation=false;
936 // save pointer to config record
937 fConfigP=aConfigP;
938 fMultiFolderDB = fConfigP->fMultiFolderDB;
939 // make a local copy of the typed agent pointer
940 fAgentP=static_cast<TCustomImplAgent *>(fSessionP);
941 // make a local copy of the typed agent config pointer
942 fAgentConfigP = DYN_CASTdynamic_cast<TCustomAgentConfig *>(
943 aSessionP->getRootConfig()->fAgentConfigP
944 );
945 if (!fAgentConfigP) SYSYNC_THROW(TSyncException(DEBUGTEXT("TCustomImplDS finds no AgentConfig","odds7")))throw TSyncException("TCustomImplDS finds no AgentConfig");
946 #ifdef SCRIPT_SUPPORT1
947 fScriptContextP=NULL__null; // no context yet
948 #endif
949 // init these keys - these might or might not be used by descendants
950 fFolderKey.erase(); // the folder key is undefined
951 fTargetKey.erase(); // the target key is undefined
952 // clear rest
953 InternalResetDataStore();
954} // TCustomImplDS::TCustomImplDS
955
956
957TCustomImplDS::~TCustomImplDS()
958{
959 InternalResetDataStore();
960} // TCustomImplDS::~TCustomImplDS
961
962
963/// @brief called while agent is still fully ok, so we must clean up such that later call of destructor does NOT access agent any more
964void TCustomImplDS::announceAgentDestruction(void)
965{
966 // reset myself
967 InternalResetDataStore();
968 // make sure we don't access the agent any more
969 // Note: as CustomImplDS always needs to be derived, we don't call
970 // engTerminateDatastore() here, but rely on descendants having done that already
971 fAgentP = NULL__null;
972 // call inherited
973 inherited::announceAgentDestruction();
974} // TCustomImplDS::announceAgentDestruction
975
976
977/// @brief called to reset datastore
978/// @note must be safe to be called multiple times and even after announceAgentDestruction()
979void TCustomImplDS::InternalResetDataStore(void)
980{
981 #ifdef SCRIPT_SUPPORT1
982 fOptionFilterTested=false; // not tested yet
983 fOptionFilterWorksOnDBLevel=true; // assume true
984 #endif
985 // delete sync set
986 DeleteSyncSet();
987 // delete finalisation queue
988 TMultiFieldItemList::iterator pos;
989 for (pos=fFinalisationQueue.begin();pos!=fFinalisationQueue.end();pos++)
990 delete (*pos); // delete the item
991 fFinalisationQueue.clear();
992 #ifndef BINFILE_ALWAYS_ACTIVE
993 fGetPhase=gph_done; // must be initialized first by startDataRead
994 fGetPhasePrepared=false;
995 // Clear map table and sync set lists
996 fMapTable.clear();
997 #endif // BINFILE_ALWAYS_ACTIVE
998 #ifdef BASED_ON_BINFILE_CLIENT1
999 fSyncSetLoaded=false;
1000 #endif // BASED_ON_BINFILE_CLIENT
1001 fNoSingleItemRead=false; // assume we can read single items
1002 if (fAgentP) {
1003 // forget script context
1004 #ifdef SCRIPT_SUPPORT1
1005 if (fScriptContextP) {
1006 delete fScriptContextP; // forget context
1007 fScriptContextP=NULL__null;
1008 }
1009 #endif
1010 }
1011 #ifdef DBAPI_TUNNEL_SUPPORT
1012 // Tunnel DB access support
1013 fTunnelReadStarted = false;
1014 #endif
1015} // TCustomImplDS::InternalResetDataStore
1016
1017
1018
1019// helper for getting a field pointer (local script var or item's field)
1020TItemField *TCustomImplDS::getMappedBaseFieldOrVar(TMultiFieldItem &aItem, sInt16 aFid)
1021{
1022 // get base field (array itself for array fields, not an element)
1023 #ifdef SCRIPT_SUPPORT1
1024 if (fScriptContextP)
1025 return fScriptContextP->getFieldOrVar(&aItem,aFid);
1026 else
1027 return aItem.getField(aFid);
1028 #else
1029 return aItem.getField(aFid);
1030 #endif
1031} // TCustomImplDS::getMappedBaseFieldOrVar
1032
1033
1034
1035// helper for getting a field pointer (local script var or item's field)
1036TItemField *TCustomImplDS::getMappedFieldOrVar(TMultiFieldItem &aItem, sInt16 aFid, sInt16 aRepOffset, bool aExistingOnly)
1037{
1038 TItemField *fieldP=NULL__null;
1039 // get field (or base field)
1040 #ifdef ARRAYFIELD_SUPPORT1
1041 fieldP = getMappedBaseFieldOrVar(aItem,aFid);
1042 if (!fieldP) return NULL__null; // no field
1043 if (fieldP->isArray()) {
1044 // use aRepOffset as array index
1045 fieldP = fieldP->getArrayField(aRepOffset,aExistingOnly);
1046 }
1047 else
1048 #endif
1049 {
1050 // use aRepOffset as fid offset
1051 #ifdef SCRIPT_SUPPORT1
1052 if (aFid<0) aRepOffset=0; // locals are never offset
1053 #endif
1054 fieldP = getMappedBaseFieldOrVar(aItem,aFid+aRepOffset);
1055 }
1056 return fieldP;
1057} // TCustomImplDS::getMappedFieldOrVar
1058
1059
1060
1061// inform logic of coming state change
1062localstatus TCustomImplDS::dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
1063{
1064 if (aNewState>=dssta_dataaccessdone && aOldState<dssta_dataaccessdone) {
1065 // ending data access
1066 #ifdef SCRIPT_SUPPORT1
1067 // Call the finalisation script for added or updated items
1068 if (fNeedFinalisation) {
1069 PDEBUGBLOCKFMT(("Finalisation","Finalizing written items","datastore=%s",getName()))getDbgLogger()->DebugOpenBlockExpanded ("Finalisation","Finalizing written items"
,"datastore=%s",getName())
;
1070 PDEBUGPRINTFX(DBG_DATA,({ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "Finalizing %ld written items"
, (long)fFinalisationQueue.size() ); }
1071 "Finalizing %ld written items",{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "Finalizing %ld written items"
, (long)fFinalisationQueue.size() ); }
1072 (long)fFinalisationQueue.size(){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "Finalizing %ld written items"
, (long)fFinalisationQueue.size() ); }
1073 )){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "Finalizing %ld written items"
, (long)fFinalisationQueue.size() ); }
;
1074 fAgentP->fScriptContextDatastore=this;
1075 while (fFinalisationQueue.size()>0) {
1076 // process finalisation script
1077 TMultiFieldItem *itemP = *(fFinalisationQueue.begin());
1078 PDEBUGBLOCKFMTCOLL(("Finalizing","Finalizing item","LocalID=%s",itemP->getLocalID()))getDbgLogger()->DebugOpenBlockCollapsed ("Finalizing","Finalizing item"
,"LocalID=%s",itemP->getLocalID())
;
1079 TScriptContext::execute(
1080 fScriptContextP,fConfigP->fFieldMappings.fFinalisationScript,fConfigP->getDSFuncTableP(),fAgentP,
1081 itemP,true // pass the item from the queue, is writable (mainly to allow fields to be passed as by-ref params)
1082 );
1083 PDEBUGENDBLOCK("Finalizing")getDbgLogger()->DebugCloseBlock( "Finalizing");
1084 // no longer needed
1085 delete itemP;
1086 // remove from queue
1087 fFinalisationQueue.erase(fFinalisationQueue.begin());
1088 }
1089 PDEBUGENDBLOCK("Finalisation")getDbgLogger()->DebugCloseBlock( "Finalisation");
1090 }
1091 // Finally, call the finish script of the field mappings
1092 fWriting=false;
1093 fInserting=false;
1094 fDeleting=false;
1095 fAgentP->fScriptContextDatastore=this;
1096 if (!TScriptContext::execute(fScriptContextP,fConfigP->fFieldMappings.fFinishScript,fConfigP->getDSFuncTableP(),fAgentP))
1097 return 510; // script error -> DB error
1098 #endif
1099 }
1100 // let inherited do its stuff as well
1101 return inherited::dsBeforeStateChange(aOldState,aNewState);
1102} // TCustomImplDS::dsBeforeStateChange
1103
1104
1105
1106// inform logic of happened state change
1107localstatus TCustomImplDS::dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
1108{
1109 if (aNewState==dssta_completed) {
1110 // completed now, call finish script
1111 #ifdef SCRIPT_SUPPORT1
1112 // process sync end script
1113 fAgentP->fScriptContextDatastore=this;
1114 TScriptContext::execute(fScriptContextP,fConfigP->fSyncEndScript,fConfigP->getDSFuncTableP(),fAgentP);
1115 #endif
1116 // reset in case that we restart
1117 DeleteSyncSet();
1118 #ifdef BASED_ON_BINFILE_CLIENT1
1119 fSyncSetLoaded=false;
1120 #endif
1121 }
1122 // let inherited do its stuff as well
1123 return inherited::dsAfterStateChange(aOldState,aNewState);
1124} // TCustomImplDS::dsAfterStateChange
1125
1126
1127#ifndef BINFILE_ALWAYS_ACTIVE
1128
1129// mark all map entries as deleted
1130bool TCustomImplDS::deleteAllMaps(void)
1131{
1132 string sql,s;
1133 bool allok=true;
1134
1135 TMapContainer::iterator pos;
1136 for (pos=fMapTable.begin();pos!=fMapTable.end();pos++) {
1137 (*pos).deleted=true; // deleted
1138 }
1139 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("deleteAllMaps: all existing map entries (%ld) now marked deleted=1",(long)fMapTable.size())){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("deleteAllMaps: all existing map entries (%ld) now marked deleted=1"
,(long)fMapTable.size()); }
;
1140 return allok;
1141} // TCustomImplDS::deleteAllMaps
1142
1143
1144// find non-deleted map entry by local ID/maptype
1145TMapContainer::iterator TCustomImplDS::findMapByLocalID(const char *aLocalID,TMapEntryType aEntryType, bool aDeletedAsWell)
1146{
1147 TMapContainer::iterator pos;
1148 if (aLocalID) {
1149 for (pos=fMapTable.begin();pos!=fMapTable.end();pos++) {
1150 if (
1151 (*pos).localid==aLocalID && (*pos).entrytype==aEntryType
1152 // && !(*pos).remoteid.empty() // Note: was ok in old versions, but now we can have map entries from resume with empty localID
1153 && (aDeletedAsWell || !(*pos).deleted) // if selected, don't show deleted entries
1154 ) {
1155 // found
1156 return pos;
1157 }
1158 }
1159 }
1160 return fMapTable.end();
1161} // TCustomImplDS::findMapByLocalID
1162
1163
1164// find map entry by remote ID
1165TMapContainer::iterator TCustomImplDS::findMapByRemoteID(const char *aRemoteID)
1166{
1167 TMapContainer::iterator pos;
1168 if (aRemoteID) {
1169 for (pos=fMapTable.begin();pos!=fMapTable.end();pos++) {
1170 if (
1171 (*pos).remoteid==aRemoteID && (*pos).entrytype == mapentry_normal && !(*pos).deleted // only plain normal non-deleted maps (no tempid or mapforresume)
1172 ) {
1173 // found
1174 return pos;
1175 }
1176 }
1177 }
1178 return fMapTable.end();
1179} // TCustomImplDS::findMapByRemoteID
1180
1181
1182#ifdef SYSYNC_SERVER1
1183
1184// - called when a item in the sync set changes its localID (due to local DB internals)
1185// Datastore must make sure that possibly cached items get updated
1186void TCustomImplDS::dsLocalIdHasChanged(const char *aOldID, const char *aNewID)
1187{
1188 // find item in map
1189 TMapContainer::iterator pos=findMapByLocalID(aOldID,mapentry_normal); // only plain maps, no deleted ones
1190 if (pos!=fMapTable.end()) {
1191 // found, modify now
1192 // NOTE: we may not modify a localid, but must delete the entry and add a new one
1193 // - get remote ID
1194 string remoteid = (*pos).remoteid;
1195 uInt32 mapflags = (*pos).mapflags;
1196 // - mark old map entry as deleted
1197 (*pos).deleted=true;
1198 // - create new one
1199 modifyMap(mapentry_normal,aNewID,remoteid.c_str(),mapflags,false);
1200 }
1201 // let base class do what is needed to update the item itself
1202 inherited::dsLocalIdHasChanged(aOldID, aNewID);
1203} // TCustomImplDS::dsLocalIdHasChanged
1204
1205#endif
1206
1207/// @brief modify internal map table
1208/// @note
1209/// - if aDelete is set, map entry will be deleted
1210/// - aClearFlags (default=all) will be cleared when updating only
1211/// - aMapFlags will be set when updating
1212/// - if aRemoteID is NULL when updating an existing (not marked deleted) item, existing remoteID will NOT be modified
1213/// - otherwise, map item will be added or updated.
1214/// - routine makes sure that there is no more than one map for each localID/entrytype and
1215/// each remoteID.
1216/// - remoteID can also be a temporary localID used by server to send to clients with too small MaxGUIDSize (mapflag_tempidmap set)
1217/// - routine will re-activate deleted entries to avoid unnecessary delete/insert
1218void TCustomImplDS::modifyMap(TMapEntryType aEntryType, const char *aLocalID, const char *aRemoteID, uInt32 aMapFlags, bool aDelete, uInt32 aClearFlags)
1219{
1220 TMapContainer::iterator pos=fMapTable.end();
1221
1222 DEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "ModifyMap called: aEntryType=%s, aLocalID='%s, aRemoteid='%s', aMapflags=0x%lX, aDelete=%d"
, MapEntryTypeNames[aEntryType], aLocalID && *aLocalID
? aLocalID : "<none>", aRemoteID ? (*aRemoteID ? aRemoteID
: "<set none>") : "<do not change>", (long)aMapFlags
, (int)aDelete ); }
1223 "ModifyMap called: aEntryType=%s, aLocalID='%s, aRemoteid='%s', aMapflags=0x%lX, aDelete=%d",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "ModifyMap called: aEntryType=%s, aLocalID='%s, aRemoteid='%s', aMapflags=0x%lX, aDelete=%d"
, MapEntryTypeNames[aEntryType], aLocalID && *aLocalID
? aLocalID : "<none>", aRemoteID ? (*aRemoteID ? aRemoteID
: "<set none>") : "<do not change>", (long)aMapFlags
, (int)aDelete ); }
1224 MapEntryTypeNames[aEntryType],{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "ModifyMap called: aEntryType=%s, aLocalID='%s, aRemoteid='%s', aMapflags=0x%lX, aDelete=%d"
, MapEntryTypeNames[aEntryType], aLocalID && *aLocalID
? aLocalID : "<none>", aRemoteID ? (*aRemoteID ? aRemoteID
: "<set none>") : "<do not change>", (long)aMapFlags
, (int)aDelete ); }
1225 aLocalID && *aLocalID ? aLocalID : "<none>",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "ModifyMap called: aEntryType=%s, aLocalID='%s, aRemoteid='%s', aMapflags=0x%lX, aDelete=%d"
, MapEntryTypeNames[aEntryType], aLocalID && *aLocalID
? aLocalID : "<none>", aRemoteID ? (*aRemoteID ? aRemoteID
: "<set none>") : "<do not change>", (long)aMapFlags
, (int)aDelete ); }
1226 aRemoteID ? (*aRemoteID ? aRemoteID : "<set none>") : "<do not change>",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "ModifyMap called: aEntryType=%s, aLocalID='%s, aRemoteid='%s', aMapflags=0x%lX, aDelete=%d"
, MapEntryTypeNames[aEntryType], aLocalID && *aLocalID
? aLocalID : "<none>", aRemoteID ? (*aRemoteID ? aRemoteID
: "<set none>") : "<do not change>", (long)aMapFlags
, (int)aDelete ); }
1227 (long)aMapFlags,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "ModifyMap called: aEntryType=%s, aLocalID='%s, aRemoteid='%s', aMapflags=0x%lX, aDelete=%d"
, MapEntryTypeNames[aEntryType], aLocalID && *aLocalID
? aLocalID : "<none>", aRemoteID ? (*aRemoteID ? aRemoteID
: "<set none>") : "<do not change>", (long)aMapFlags
, (int)aDelete ); }
1228 (int)aDelete{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "ModifyMap called: aEntryType=%s, aLocalID='%s, aRemoteid='%s', aMapflags=0x%lX, aDelete=%d"
, MapEntryTypeNames[aEntryType], aLocalID && *aLocalID
? aLocalID : "<none>", aRemoteID ? (*aRemoteID ? aRemoteID
: "<set none>") : "<do not change>", (long)aMapFlags
, (int)aDelete ); }
1229 )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "ModifyMap called: aEntryType=%s, aLocalID='%s, aRemoteid='%s', aMapflags=0x%lX, aDelete=%d"
, MapEntryTypeNames[aEntryType], aLocalID && *aLocalID
? aLocalID : "<none>", aRemoteID ? (*aRemoteID ? aRemoteID
: "<set none>") : "<do not change>", (long)aMapFlags
, (int)aDelete ); }
;
1230 // - if there is a localID, search map entry (even if it is deleted)
1231 if (aLocalID && *aLocalID!=0) {
1232 for (pos=fMapTable.begin();pos!=fMapTable.end();pos++) {
1233 if (
1234 // localID and entrytype matches
1235 (*pos).localid==aLocalID && (*pos).entrytype==aEntryType
1236 ) {
1237 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aLocalID, (*pos).remoteid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1238 "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aLocalID, (*pos).remoteid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1239 aLocalID,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aLocalID, (*pos).remoteid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1240 (*pos).remoteid.c_str(),{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aLocalID, (*pos).remoteid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1241 (long)(*pos).mapflags,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aLocalID, (*pos).remoteid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1242 (int)(*pos).changed,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aLocalID, (*pos).remoteid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1243 (int)(*pos).deleted,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aLocalID, (*pos).remoteid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1244 (int)(*pos).added,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aLocalID, (*pos).remoteid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1245 (int)(*pos).markforresume,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aLocalID, (*pos).remoteid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1246 (int)(*pos).savedmark{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aLocalID, (*pos).remoteid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1247 )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aLocalID, (*pos).remoteid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
;
1248 break;
1249 }
1250 }
1251 }
1252 else aLocalID=NULL__null;
1253 // decide what to do
1254 if (aDelete) {
1255 // delete
1256 if (!aLocalID) {
1257 if (!aRemoteID) return; // nop
1258 // we need to search by remoteID first
1259 pos=findMapByRemoteID(aRemoteID);
1260 #ifdef SYDEBUG2
1261 if (pos!=fMapTable.end()) {
1262 DEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aRemoteID, (*pos).localid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1263 "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aRemoteID, (*pos).localid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1264 aRemoteID,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aRemoteID, (*pos).localid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1265 (*pos).localid.c_str(),{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aRemoteID, (*pos).localid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1266 (long)(*pos).mapflags,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aRemoteID, (*pos).localid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1267 (int)(*pos).changed,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aRemoteID, (*pos).localid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1268 (int)(*pos).deleted,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aRemoteID, (*pos).localid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1269 (int)(*pos).added,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aRemoteID, (*pos).localid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1270 (int)(*pos).markforresume,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aRemoteID, (*pos).localid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1271 (int)(*pos).savedmark{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aRemoteID, (*pos).localid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
1272 )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, aRemoteID, (*pos).localid.c_str(), (long)(*pos).mapflags, (
int)(*pos).changed, (int)(*pos).deleted, (int)(*pos).added, (
int)(*pos).markforresume, (int)(*pos).savedmark ); }
;
1273 }
1274 #endif
1275 }
1276 if (pos==fMapTable.end()) return; // not found, nop
1277 // mark deleted
1278 if ((*pos).added) {
1279 // has been added in this session and not yet saved
1280 // so it does not yet exist in the DB at all
1281 // - simply forget entry
1282 fMapTable.erase(pos);
1283 // - done, ok
1284 return;
1285 }
1286 // entry has already been saved to DB before - only mark deleted
1287 (*pos).deleted=true;
1288 } // delete
1289 else {
1290 // update or add
1291 if (pos==fMapTable.end()) {
1292 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found no matching entry for localID '%s' - creating new one, added=true"
, aLocalID ); }
1293 "- found no matching entry for localID '%s' - creating new one, added=true",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found no matching entry for localID '%s' - creating new one, added=true"
, aLocalID ); }
1294 aLocalID{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found no matching entry for localID '%s' - creating new one, added=true"
, aLocalID ); }
1295 )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- found no matching entry for localID '%s' - creating new one, added=true"
, aLocalID ); }
;
1296 // add, because there is not yet an item with that localid/entrytype
1297 TMapEntry entry;
1298 entry.entrytype=aEntryType;
1299 entry.added=true;
1300 entry.changed=true;
1301 entry.deleted=false;
1302 entry.localid=aLocalID;
1303 AssignString(entry.remoteid,aRemoteID); // if NULL, remoteID will be empty
1304 entry.savedmark=false;
1305 entry.markforresume=false;
1306 entry.mapflags=0; // none set by default
1307 fMapTable.push_front(entry);
1308 pos=fMapTable.begin(); // first entry is new entry
1309 }
1310 else {
1311 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- matching entry found - re-activating deleted and/or updating contents if needed"
); }
1312 "- matching entry found - re-activating deleted and/or updating contents if needed"{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- matching entry found - re-activating deleted and/or updating contents if needed"
); }
1313 )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- matching entry found - re-activating deleted and/or updating contents if needed"
); }
;
1314 /* %%% NO!!!! plain wrong - because this causes added and later changed entries never be added to the DB!
1315 // %%% No room for paranoia here - makes things worse!
1316 // just to make sure - added should not be set here
1317 // (if we delete an added one before, it will be completely deleted from the list again)
1318 (*pos).added=false;
1319 */
1320 // check if contents change, update if so
1321 if (
1322 (((*pos).mapflags & ~mapflag_useforresume0x00000001) != aMapFlags) || // flags different (useForResume not tested!)
1323 (aRemoteID && !((*pos).remoteid==aRemoteID)) // remoteID different
1324 ) {
1325 // new RemoteID (but not NULL = keep existing) or different mapflags were passed -> this is a real change
1326 if (aRemoteID)
1327 (*pos).remoteid=aRemoteID;
1328 (*pos).changed=true; // really changed compared to what is already in DB
1329 }
1330 }
1331 // now item exists, set details
1332 (*pos).deleted=false; // in case we had it deleted before, but not yet saved
1333 // clear those flags shown in aClearFlags (by default: all) and set those in aMapFlags
1334 (*pos).mapflags = ((*pos).mapflags & ~aClearFlags) | aMapFlags;
1335 // now remove all other items with same remoteID (except if we have no or empty remoteID)
1336 if (aEntryType==mapentry_normal && aRemoteID && *aRemoteID) {
1337 // %%% note: this is strictly necessary only for add, but cleans up for update
1338 TMapContainer::iterator pos2;
1339 for (pos2=fMapTable.begin();pos2!=fMapTable.end();pos2++) {
1340 if (pos2!=pos && (*pos2).remoteid==aRemoteID && (*pos2).entrytype==aEntryType) {
1341 // found another one with same remoteID/entrytype
1342 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- cleanup: removing same remoteID from other entry with localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, (*pos2).localid.c_str(), (long)(*pos2).mapflags, (int)(*pos2
).changed, (int)(*pos2).deleted, (int)(*pos2).added, (int)(*pos2
).markforresume, (int)(*pos2).savedmark ); }
1343 "- cleanup: removing same remoteID from other entry with localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- cleanup: removing same remoteID from other entry with localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, (*pos2).localid.c_str(), (long)(*pos2).mapflags, (int)(*pos2
).changed, (int)(*pos2).deleted, (int)(*pos2).added, (int)(*pos2
).markforresume, (int)(*pos2).savedmark ); }
1344 (*pos2).localid.c_str(),{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- cleanup: removing same remoteID from other entry with localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, (*pos2).localid.c_str(), (long)(*pos2).mapflags, (int)(*pos2
).changed, (int)(*pos2).deleted, (int)(*pos2).added, (int)(*pos2
).markforresume, (int)(*pos2).savedmark ); }
1345 (long)(*pos2).mapflags,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- cleanup: removing same remoteID from other entry with localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, (*pos2).localid.c_str(), (long)(*pos2).mapflags, (int)(*pos2
).changed, (int)(*pos2).deleted, (int)(*pos2).added, (int)(*pos2
).markforresume, (int)(*pos2).savedmark ); }
1346 (int)(*pos2).changed,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- cleanup: removing same remoteID from other entry with localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, (*pos2).localid.c_str(), (long)(*pos2).mapflags, (int)(*pos2
).changed, (int)(*pos2).deleted, (int)(*pos2).added, (int)(*pos2
).markforresume, (int)(*pos2).savedmark ); }
1347 (int)(*pos2).deleted,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- cleanup: removing same remoteID from other entry with localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, (*pos2).localid.c_str(), (long)(*pos2).mapflags, (int)(*pos2
).changed, (int)(*pos2).deleted, (int)(*pos2).added, (int)(*pos2
).markforresume, (int)(*pos2).savedmark ); }
1348 (int)(*pos2).added,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- cleanup: removing same remoteID from other entry with localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, (*pos2).localid.c_str(), (long)(*pos2).mapflags, (int)(*pos2
).changed, (int)(*pos2).deleted, (int)(*pos2).added, (int)(*pos2
).markforresume, (int)(*pos2).savedmark ); }
1349 (int)(*pos2).markforresume,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- cleanup: removing same remoteID from other entry with localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, (*pos2).localid.c_str(), (long)(*pos2).mapflags, (int)(*pos2
).changed, (int)(*pos2).deleted, (int)(*pos2).added, (int)(*pos2
).markforresume, (int)(*pos2).savedmark ); }
1350 (int)(*pos2).savedmark{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- cleanup: removing same remoteID from other entry with localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, (*pos2).localid.c_str(), (long)(*pos2).mapflags, (int)(*pos2
).changed, (int)(*pos2).deleted, (int)(*pos2).added, (int)(*pos2
).markforresume, (int)(*pos2).savedmark ); }
1351 )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "- cleanup: removing same remoteID from other entry with localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, (*pos2).localid.c_str(), (long)(*pos2).mapflags, (int)(*pos2
).changed, (int)(*pos2).deleted, (int)(*pos2).added, (int)(*pos2
).markforresume, (int)(*pos2).savedmark ); }
;
1352 // this remoteID is invalid for sure as we just have assigned it to another item - remove it
1353 (*pos2).remoteid.erase();
1354 (*pos2).changed=true; // make sure it gets saved
1355 }
1356 }
1357 }
1358 } // modify or add
1359} // TCustomImplDS::modifyMap
1360
1361
1362#endif // not BINFILE_ALWAYS_ACTIVE
1363
1364
1365// delete syncset
1366// - if aContentsOnly, only item data will be deleted, but localID/containerid will
1367// be retained
1368void TCustomImplDS::DeleteSyncSet(bool aContentsOnly)
1369{
1370 TSyncSetList::iterator pos;
1371 for (pos=fSyncSetList.begin();pos!=fSyncSetList.end();pos++) {
1372 // delete contained item, if any
1373 if ((*pos)->itemP) {
1374 delete ((*pos)->itemP);
1375 (*pos)->itemP=NULL__null;
1376 }
1377 if (!aContentsOnly)
1378 delete (*pos); // delete syncsetitem itself
1379 }
1380 if (!aContentsOnly) fSyncSetList.clear();
1381} // TCustomImplDS::DeleteSyncSet
1382
1383
1384// - get container ID for specified localid
1385bool TCustomImplDS::getContainerID(const char *aLocalID, string &aContainerID)
1386{
1387 TSyncSetList::iterator pos = findInSyncSet(aLocalID);
1388 if (pos!=fSyncSetList.end()) {
1389 aContainerID = (*pos)->containerid;
1390 return true; // found
1391 }
1392 return false; // not found
1393} // TCustomImplDS::getContainerID
1394
1395
1396// find entry in sync set by localid
1397TSyncSetList::iterator TCustomImplDS::findInSyncSet(const char *aLocalID)
1398{
1399 TSyncSetList::iterator pos;
1400 for (pos=fSyncSetList.begin();pos!=fSyncSetList.end();pos++) {
1401 if ((*pos)->localid==aLocalID) {
1402 // found
1403 return pos;
1404 }
1405 }
1406 return fSyncSetList.end();
1407} // TCustomImplDS::findInSyncSet
1408
1409
1410// called when message processing
1411void TCustomImplDS::dsEndOfMessage(void)
1412{
1413 // let ancestor do things
1414 inherited::dsEndOfMessage();
1415} // TCustomImplDS::dsEndOfMessage
1416
1417
1418
1419// Simple DB access interface methods
1420
1421
1422// - returns true if database implementation can only update all fields of a record at once
1423bool TCustomImplDS::dsReplaceWritesAllDBFields(void)
1424{
1425 // return true if we should read record from DB before replacing.
1426 return fConfigP->fUpdateAllFields;
1427} // TCustomImplDS::dsReplaceWritesAllDBFields
1428
1429
1430#ifndef BINFILE_ALWAYS_ACTIVE
1431
1432// returns true if DB implementation supports resume (saving of resume marks, alert code, pending maps, tempGUIDs)
1433bool TCustomImplDS::dsResumeSupportedInDB(void)
1434{
1435 #ifdef BASED_ON_BINFILE_CLIENT1
1436 if (binfileDSActive())
1437 return inherited::dsResumeSupportedInDB();
1438 else
1439 #endif
1440 return fConfigP && fConfigP->fResumeSupport;
1441} // TCustomImplDS::dsResumeSupportedInDB
1442
1443
1444// returns true if DB implementation supports resuming in midst of a chunked item (can save fPIxxx.. and related admin data)
1445bool TCustomImplDS::dsResumeChunkedSupportedInDB(void)
1446{
1447 #ifdef BASED_ON_BINFILE_CLIENT1
1448 if (binfileDSActive())
1449 return inherited::dsResumeChunkedSupportedInDB();
1450 else
1451 #endif
1452 return fConfigP && fConfigP->fResumeItemSupport;
1453} // TCustomImplDS::dsResumeChunkedSupportedInDB
1454
1455#endif // BINFILE_ALWAYS_ACTIVE
1456
1457
1458#ifdef OBJECT_FILTERING1
1459
1460// - returns true if DB implementation can also apply special filters like CGI-options
1461// /dr(x,y) etc. during fetching
1462bool TCustomImplDS::dsOptionFilterFetchesFromDB(void)
1463{
1464 #ifndef SYSYNC_TARGET_OPTIONS1
1465 // there are no ranges to filter at all
1466 return true; // we can "filter" this (nothing)
1467 #else
1468 // no filter range set: yes, we can filter
1469 if (fDateRangeStart==0 && fDateRangeEnd==0) return true; // we can "filter" this
1470 // see if a script provides a solution
1471 #ifdef SCRIPT_SUPPORT1
1472 if (!fOptionFilterTested) {
1473 fOptionFilterTested=true;
1474 // call script to take measures such that database implementation can
1475 // filter, returns true if filtering is entirely possible
1476 // (e.g. for ODBC, script should generate appropriate WHERE clause and set it with SETSQLFILTER())
1477 fAgentP->fScriptContextDatastore=this;
1478 fOptionFilterWorksOnDBLevel = TScriptContext::executeTest(
1479 false, // assume we cannot filter if no script or script returns nothing
1480 fScriptContextP, // context
1481 fConfigP->fFieldMappings.fOptionFilterScript, // the script
1482 fConfigP->getDSFuncTableP(),fAgentP // funcdefs/context
1483 );
1484 }
1485 if (fOptionFilterWorksOnDBLevel) return true;
1486 #endif
1487 // we can't filter, let anchestor try
1488 return inherited::dsOptionFilterFetchesFromDB();
1489 #endif
1490} // TCustomImplDS::dsOptionFilterFetchesFromDB
1491
1492
1493#endif // OBJECT_FILTERING
1494
1495
1496
1497/// sync login (into this database)
1498/// @note might be called several times (auth retries at beginning of session)
1499/// @note must update the following saved AND current state variables
1500/// - in TLocalEngineDS: fLastRemoteAnchor, fLastLocalAnchor, fResumeAlertCode, fFirstTimeSync
1501/// - for client: fPendingAddMaps
1502/// - for server: fTempGUIDMap
1503/// - in TStdLogicDS: fPreviousSyncTime, fCurrentSyncTime
1504/// - in TCustomImplDS: fCurrentSyncCmpRef, fCurrentSyncIdentifier, fPreviousToRemoteSyncCmpRef,
1505/// fPreviousToRemoteSyncIdentifier, fPreviousSuspendCmpRef, fPreviousSuspendIdentifier
1506/// - in derived classes: whatever else belongs to dsSavedAdmin and dsCurrentAdmin state
1507localstatus TCustomImplDS::implMakeAdminReady(
1508 const char *aDeviceID, // remote device URI (device ID)
1509 const char *aDatabaseID, // database ID
1510 const char *aRemoteDBID // database ID of remote device
1511)
1512{
1513 localstatus sta=LOCERR_OK; // assume ok
1514 string sql;
1515
1516
1517 // init state variables
1518 // dsSavedAdmin
1519 // - of TLocalEngineDS:
1520 fFirstTimeSync=true; // assume first time sync
1521 fLastRemoteAnchor.erase(); // remote anchor not known yet
1522 fResumeAlertCode=0; // no session to resume
1523 // - of TStdLogicDS:
1524 fPreviousSyncTime=0;
1525 // - of TCustomImplDS:
1526 fPreviousToRemoteSyncCmpRef=0; // no previous session
1527 fPreviousToRemoteSyncIdentifier.erase();
1528 fPreviousSuspendCmpRef=0; // no previous suspend
1529 fPreviousSuspendIdentifier.erase();
1530
1531 // dsCurrentAdmin
1532 // - of TLocalEngineDS:
1533 // - of TStdLogicDS:
1534 fCurrentSyncTime=0;
1535 // - of TCustomImplDS:
1536 fCurrentSyncCmpRef=0;
1537 fCurrentSyncIdentifier.erase();
1538
1539 #ifndef BINFILE_ALWAYS_ACTIVE
1540 fMapTable.clear(); // map is empty to begin with
1541 #endif
1542 // now get admin data
1543 SYSYNC_TRYtry {
1544 #ifdef SCRIPT_SUPPORT1
1545 // rebuild context for all scripts (if not already resolved)
1546 // Note: unlike while reading config, here all maps and scripts are already available
1547 // so this will build the entire context at once.
1548 if (!fScriptContextP) {
1549 // Rebuild order MUST be same as resolving order (see ResolveDSScripts())
1550 // - scripts in possible derivates
1551 apiRebuildScriptContexts();
1552 // - adminready and end scripts outside the fieldmappings
1553 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fAdminReadyScript,fScriptContextP,fSessionP);
1554 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fSyncEndScript,fScriptContextP,fSessionP);
1555 // - scripts within fieldmappings
1556 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fOptionFilterScript,fScriptContextP,fSessionP);
1557 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fInitScript,fScriptContextP,fSessionP);
1558 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fAfterReadScript,fScriptContextP,fSessionP);
1559 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fBeforeWriteScript,fScriptContextP,fSessionP);
1560 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fAfterWriteScript,fScriptContextP,fSessionP);
1561 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fFinishScript,fScriptContextP,fSessionP);
1562 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fFinalisationScript,fScriptContextP,fSessionP);
1563 #ifdef ARRAYDBTABLES_SUPPORT1
1564 // - rebuild array script vars
1565 TFieldMapList::iterator pos;
1566 for (pos=fConfigP->fFieldMappings.fFieldMapList.begin(); pos!=fConfigP->fFieldMappings.fFieldMapList.end(); pos++) {
1567 if ((*pos)->isArray()) {
1568 TFieldMapArrayItem *fmaiP = DYN_CASTdynamic_cast<TFieldMapArrayItem *>(*pos);
1569 if (fmaiP) {
1570 // rebuild
1571 // %%% note, this is not capable of nested arrays yet
1572 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fmaiP->fInitScript,fScriptContextP,fSessionP);
1573 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fmaiP->fAfterReadScript,fScriptContextP,fSessionP);
1574 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fmaiP->fBeforeWriteScript,fScriptContextP,fSessionP);
1575 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fmaiP->fAfterWriteScript,fScriptContextP,fSessionP);
1576 TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fmaiP->fFinishScript,fScriptContextP,fSessionP);
1577 }
1578 }
1579 }
1580 #endif
1581 // now instantiate variables
1582 TScriptContext::buildVars(fScriptContextP);
1583 }
1584 #endif
1585 #ifdef BASED_ON_BINFILE_CLIENT1
1586 if (binfileDSActive()) {
1587 // binfile's implMakeAdminReady will do the job
1588 sta = inherited::implMakeAdminReady(aDeviceID, aDatabaseID, aRemoteDBID);
1589 }
1590 else
1591 #endif // BASED_ON_BINFILE_CLIENT
1592 {
1593 #ifndef BINFILE_ALWAYS_ACTIVE
1594 // Load admin data from TXXXApiDS (ODBC, text or derived class' special implementation)
1595 sta = apiLoadAdminData(
1596 aDeviceID, // remote device URI (device ID)
1597 aDatabaseID, // database ID
1598 aRemoteDBID // database ID of remote device
1599 );
1600 #endif
1601 }
1602 // set error if one occurred during load
1603 if (sta==LOCERR_OK) {
1604 // extra check: if we get empty remote anchor, this is a first-time sync even if DB claims the opposite
1605 if (fLastRemoteAnchor.empty())
1606 fFirstTimeSync=true;
1607 // create identifier if we don't have it stored in the DB
1608 if (!fConfigP->fStoreSyncIdentifiers) {
1609 // we generate the identifiers as ISO8601 UTC string from the timestamp
1610 TimestampToISO8601Str(fPreviousToRemoteSyncIdentifier,fPreviousToRemoteSyncCmpRef,TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),false,false);
1611 TimestampToISO8601Str(fPreviousSuspendIdentifier,fPreviousSuspendCmpRef,TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),false,false);
1612 }
1613 // determine time of this sync
1614 fCurrentSyncTime=
1615 fAgentP->getDatabaseNowAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)));
1616 // by default, these two are equal
1617 // (but if DB cannot write items with timestamp exactly==fCurrentSyncTime, fCurrentSyncCmpRef might be a later time, like end-of-session)
1618 fCurrentSyncCmpRef = fCurrentSyncTime;
1619 // admin ready now, call script that may access DB to fetch some extra options
1620 #ifdef SCRIPT_SUPPORT1
1621 fAgentP->fScriptContextDatastore=this;
1622 if (!TScriptContext::executeTest(true,fScriptContextP,fConfigP->fAdminReadyScript,fConfigP->getDSFuncTableP(),fAgentP))
1623 sta=510; // script returns false or fails -> DB error
1624 #endif
1625 if (sta==LOCERR_OK) {
1626 // successful so far, now allow for early startDataRead to occur if configured on api level
1627 sta = apiEarlyDataAccessStart();
1628 if (sta==508) {
1629 // special case: the database requests a slow sync for internal reasons (like change tracking disabled)
1630 // - force slow sync by removing last anchor
1631 fLastRemoteAnchor.erase();
1632 sta = LOCERR_OK;
1633 }
1634 }
1635 } // if apiLoadAdminData successful
1636 }
1637 SYSYNC_CATCH(exception &e)catch(exception &e) {
1638 PDEBUGPRINTFX(DBG_ERROR,("implMakeAdminReady exception: %s",e.what())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("implMakeAdminReady exception: %s"
,e.what()); }
;
1639 sta=510;
1640 SYSYNC_ENDCATCH}
1641 // done
1642 return sta;
1643} // TCustomImplDS::implMakeAdminReady
1644
1645
1646
1647// start data read
1648localstatus TCustomImplDS::implStartDataRead()
1649{
1650 localstatus sta = LOCERR_OK;
1651
1652 // get field map list
1653 TFieldMapList &fml = fConfigP->fFieldMappings.fFieldMapList;
1654
1655 // check if we have fileds that must be finalized or array fields at all (to avoid unneeded operations if not)
1656 TFieldMapList::iterator pos;
1657 #ifdef ARRAYDBTABLES_SUPPORT1
1658 fHasArrayFields=false; // until we KNOW otherwise
1659 #endif
1660 fNeedFinalisation=false; // until we KNOW otherwise
1661 for (pos=fml.begin(); pos!=fml.end(); pos++) {
1662 // - check finalisation
1663 if ((*pos)->needs_finalisation)
1664 fNeedFinalisation=true;
1665 // - check array mappings
1666 #ifdef ARRAYDBTABLES_SUPPORT1
1667 if ((*pos)->isArray())
1668 fHasArrayFields=true;
1669 #endif
1670 }
1671 #ifdef BASED_ON_BINFILE_CLIENT1
1672 if (binfileDSActive()) {
1673 // further preparation is in binfileds
1674 sta = inherited::implStartDataRead();
1675 if (sta==LOCERR_OK) {
1676 // now make sure the syncset is loaded
1677 sta = makeSyncSetLoaded(
1678 fSlowSync // all items with data needed for slow sync
1679 #ifdef OBJECT_FILTERING1
1680 || fFilteringNeededForAll // all item data needed for dynamic filtering
1681 #endif
1682 || CRC_CHANGE_DETECTION(fConfigP->fCRCChangeDetection) // all item data needed when binfile must detect changes using CRC
1683 );
1684 }
1685 }
1686 else
1687 #endif // BASED_ON_BINFILE_CLIENT
1688 {
1689 #ifndef BINFILE_ALWAYS_ACTIVE
1690 // kill all map entries if slow sync (but not if resuming!!)
1691 if (fSlowSync && !isResuming()) {
1692 // mark all map entries as deleted
1693 deleteAllMaps();
1694 }
1695 // - count entire read as database read
1696 TP_DEFIDX(li);
1697 TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
1698 PDEBUGBLOCKFMTCOLL(("ReadSyncSet","Reading Sync Set from Database","datastore=%s",getName()))getDbgLogger()->DebugOpenBlockCollapsed ("ReadSyncSet","Reading Sync Set from Database"
,"datastore=%s",getName())
;
1699 SYSYNC_TRYtry {
1700 // read sync set (maybe from derived non-odbc data source)
1701 // - in slow sync, we need all items (so allow ReadSyncSet to read them all here)
1702 // - if all items must be filtered, we also need all data
1703 // Note: ReadSyncSet will decide if it actually needs to load the syncset or not (depends on refresh, slowsync and needs of apiZapSyncSet())
1704 sta = apiReadSyncSet(
1705 fSlowSync
1706 #ifdef OBJECT_FILTERING1
1707 || fFilteringNeededForAll
1708 #endif
1709 );
1710 // determine how GetItem will start
1711 fGetPhase = fSlowSync ? gph_added_changed : gph_deleted; // just report added (not-in-map, map is cleared already) for slowsync
1712 // phase not yet prepared
1713 fGetPhasePrepared = false;
1714 // end of DB read
1715 PDEBUGENDBLOCK("ReadSyncSet")getDbgLogger()->DebugCloseBlock( "ReadSyncSet");
1716 TP_START(fSessionP->fTPInfo,li);
1717 }
1718 SYSYNC_CATCH(exception &e)catch(exception &e) {
1719 PDEBUGPRINTFX(DBG_ERROR,("StartDataRead exception: %s",e.what())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("StartDataRead exception: %s"
,e.what()); }
;
1720 sta=510;
1721 // end of DB read
1722 PDEBUGENDBLOCK("ReadSyncSet")getDbgLogger()->DebugCloseBlock( "ReadSyncSet");
1723 TP_START(fSessionP->fTPInfo,li);
1724 SYSYNC_ENDCATCH}
1725 #endif // BINFILE_ALWAYS_ACTIVE
1726 }
1727 return sta;
1728} // TCustomImplDS::implStartDataRead
1729
1730
1731
1732// Queue the data needed for finalisation (usually - relational link updates)
1733// as a item copy with only finalisation-required fields
1734void TCustomImplDS::queueForFinalisation(TMultiFieldItem *aItemP)
1735{
1736 sInt16 fid;
1737 // create a same-typed copy of the original item (initially empty)
1738 TMultiFieldItem *itemP = new TMultiFieldItem(aItemP->getItemType(),aItemP->getTargetItemType());
1739 // copy localID and syncop
1740 itemP->setLocalID(aItemP->getLocalID());
1741 itemP->setSyncOp(aItemP->getSyncOp());
1742 // copy fields that are marked for finalisation
1743 TFieldMapList &fml = fConfigP->fFieldMappings.fFieldMapList;
1744 TFieldMapList::iterator pos;
1745 for (pos=fml.begin(); pos!=fml.end(); pos++) {
1746 TFieldMapItem *fmiP = *pos;
1747 #ifdef ARRAYDBTABLES_SUPPORT1
1748 if (fmiP->isArray()) {
1749 TFieldMapList::iterator pos2;
1750 TFieldMapList &afml = static_cast<TFieldMapArrayItem *>(fmiP)->fArrayFieldMapList;
1751 for (pos2=afml.begin(); pos2!=afml.end(); pos2++) {
1752 TFieldMapItem *fmi2P = *pos2;
1753 fid = fmi2P->fid;
1754 if (fmi2P->needs_finalisation && fid>=0) {
1755 // this mapping indicates need for finalisation and references a fieldlist field, copy referenced field
1756 *(itemP->getField(fid))=*(aItemP->getField(fid));
1757 }
1758 }
1759 }
1760 else
1761 #endif // ARRAYDBTABLES_SUPPORT
1762 {
1763 fid = fmiP->fid;
1764 if (fmiP->needs_finalisation && fid>=0) {
1765 // this mapping indicates need for finalisation and references a fieldlist field, copy referenced field
1766 *(itemP->getField(fid))=*(aItemP->getField(fid));
1767 }
1768 }
1769 }
1770 // put the finalisation item into the queue
1771 fFinalisationQueue.push_back(itemP);
1772} // TCustomImplDS::queueForFinalisation
1773
1774
1775
1776#ifndef BINFILE_ALWAYS_ACTIVE
1777
1778
1779/// @brief called to have all non-yet-generated sync commands as "to-be-resumed"
1780void TCustomImplDS::implMarkOnlyUngeneratedForResume(void)
1781{
1782 #ifdef BASED_ON_BINFILE_CLIENT1
1783 // let binfile handle it if it is active
1784 if (binfileDSActive()) {
1785 inherited::implMarkOnlyUngeneratedForResume();
1786 return;
1787 }
1788 #endif // BASED_ON_BINFILE_CLIENT
1789
1790 // Note: all "markforresume" flags (but NOT the actual mapflag_useforresume!) are cleared
1791 // after loading or saving admin, so we can start adding resume marks BEFORE
1792 // implMarkOnlyUngeneratedForResume is called (needed to re-add items that got
1793 // an unsuccessful status from remote that suggests re-trying in next resume, such as 514)
1794 // add all not-yet-got items
1795 TMapContainer::iterator pos;
1796 TGetPhases getPhase = fGetPhase; // start at current get phase
1797 bool getPrepared = fGetPhasePrepared;
1798 // now flag all deletes that need resuming
1799 if (getPhase==gph_deleted) {
1800 // now mark pending deletes; if we are still in "deleted" phase, add these first
1801 for (pos = !getPrepared ? fMapTable.begin() : fDeleteMapPos;
1802 pos!=fMapTable.end();
1803 ++pos) {
1804 // check only undeleted map entries
1805 // Note: non-normal maps are always in deleted state in fMapTable, so these will be skipped as well
1806 if ((*pos).deleted) continue;
1807 // check if deleted
1808 if (findInSyncSet((*pos).localid.c_str())==fSyncSetList.end()) {
1809 // mark this as pending for resume
1810 (*pos).markforresume=true;
1811 }
1812 }
1813 getPhase=gph_added_changed;
1814 getPrepared=false;
1815 }
1816 // now flag all changes and adds that need resuming
1817 // if we are already gph_done, no items need to be flagged here
1818 if (getPhase==gph_added_changed) {
1819 // if we are in the add/change phase, add not-yet handled adds/changes
1820 TSyncSetList::iterator syncsetpos;
1821 if (!getPrepared)
1822 syncsetpos=fSyncSetList.begin();
1823 else
1824 syncsetpos=fSyncSetPos;
1825 // now mark pending changes and adds
1826 while (syncsetpos!=fSyncSetList.end()) {
1827 // check if we need to mark it
1828 bool needMark=false;
1829 pos=findMapByLocalID((*syncsetpos)->localid.c_str(),mapentry_normal,true); // find deleted ones as well
1830 if (fSlowSync) {
1831 if (IS_CLIENT(!getSyncAppBase()->isServer())) {
1832 // for client, there are no reference-only: mark all leftovers in a slow sync
1833 needMark=true;
1834 }
1835 else {
1836 // for server, make sure not to mark reference-only.
1837 if (!isResuming() || pos==fMapTable.end()) {
1838 // if not resuming, or we have no map for this one at all - we'll need it again for resume
1839 needMark=true;
1840 }
1841 else {
1842 // for slowsync resume which have already a map:
1843 // - items that are not marked for resume, but already have a remoteID mapped
1844 // are reference-only and must NOT be marked
1845 if (((*pos).mapflags & mapflag_useforresume0x00000001) || (*pos).remoteid.empty())
1846 needMark=true;
1847 }
1848 }
1849 }
1850 else if (!isRefreshOnly() || (isRefreshOnly() && isCacheData())) {
1851 // not slow sync, and not refresh from remote only - mark those that are actually are involved
1852 if (pos!=fMapTable.end()) {
1853 // known item, needs a mark only if record is modified (and updates reported at all)
1854 needMark=((*syncsetpos)->isModified) && fReportUpdates;
1855 }
1856 else {
1857 // adds need marking, anyway
1858 needMark=true;
1859 }
1860 }
1861 // now apply mark if needed
1862 if (needMark) {
1863 if (pos==fMapTable.end()) {
1864 // no map entry for this item yet (this means that this is a new detected add
1865 // - add pendingAddConfirm item now, and mark it for resume
1866 TMapEntry entry;
1867 entry.entrytype=mapentry_normal;
1868 entry.localid=(*syncsetpos)->localid.c_str();
1869 entry.remoteid.erase();
1870 entry.mapflags=mapflag_pendingAddConfirm0x00000010;
1871 entry.added=true;
1872 entry.changed=true;
1873 entry.deleted=false;
1874 entry.markforresume=true;
1875 entry.savedmark=false;
1876 fMapTable.push_back(entry);
1877 }
1878 else {
1879 // add flag to existing map item
1880 if ((*pos).deleted) {
1881 // undelete (re-use existing, but currently invalid entry)
1882 (*pos).remoteid.erase();
1883 (*pos).changed=true;
1884 (*pos).deleted=false;
1885 (*pos).mapflags=0;
1886 }
1887 (*pos).markforresume=true;
1888 }
1889 }
1890 // next
1891 ++syncsetpos;
1892 }
1893 }
1894} // TCustomImplDS::implMarkOnlyUngeneratedForResume
1895
1896
1897// called to confirm a sync operation's completion (status from remote received)
1898// @note aSyncOp passed not necessarily reflects what was sent to remote, but what actually happened
1899void TCustomImplDS::dsConfirmItemOp(TSyncOperation aSyncOp, cAppCharP aLocalID, cAppCharP aRemoteID, bool aSuccess, localstatus aErrorStatus)
1900{
1901 #ifdef BASED_ON_BINFILE_CLIENT1
1902 // let binfile handle it if it is active
1903 if (binfileDSActive()) {
1904 inherited::dsConfirmItemOp(aSyncOp, aLocalID, aRemoteID, aSuccess, aErrorStatus);
1905 return;
1906 }
1907 #endif // BASED_ON_BINFILE_CLIENT
1908
1909 if (aSyncOp==sop_delete || aSyncOp==sop_archive_delete) {
1910 // a confirmed delete causes the entire map entry to be removed (item no longer exists (or is visible) locally or remotely)
1911 if (aSuccess) {
1912 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("successful status for delete received -> delete map entry now")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("successful status for delete received -> delete map entry now"
); }
;
1913 modifyMap(mapentry_normal,aLocalID,aRemoteID,0,true);
1914 }
1915 }
1916 else {
1917 TMapContainer::iterator pos;
1918 if (IS_CLIENT(!getSyncAppBase()->isServer())) {
1919 // for client, always find by localid
1920 pos=findMapByLocalID(aLocalID,mapentry_normal);
1921 }
1922 else {
1923 // for server, only add can be found by localid
1924 if (aSyncOp==sop_add)
1925 pos=findMapByLocalID(aLocalID,mapentry_normal);
1926 else
1927 pos=findMapByRemoteID(aRemoteID);
1928 }
1929 if (pos!=fMapTable.end()) {
1930 // Anyway, clear the status pending flag
1931 // Note: we do not set the "changed" bit here because we don't really need to make this persistent between sessions
1932 (*pos).mapflags &= ~mapflag_pendingStatus0x00000004;
1933 if (IS_CLIENT(!getSyncAppBase()->isServer())) {
1934 if (aSuccess) {
1935 // Note: we do not check for sop here - any successfully statused sop will clear the pending add status
1936 // (e.g. in slow sync, items reported as add to engine are actually sent as replaces, but still
1937 // seeing a ok status means that they are not any longer pending as adds)
1938 // Note: the same functionality formerly was in TStdLogicDS::startDataWrite() - making sure that a
1939 // add sent to the server is not repeated. As every item reported by implGetItem now already
1940 // has a map entry (adds get one with mapflag_pendingAddConfirm set), we just need to clear
1941 // the flag here now that we know the add has reached the server.
1942 // Note: For the server, we can clear the mapflag_pendingAddConfirm not before we have received a <Map> item for it!
1943 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("successful status for non-delete received -> clear mapflag_pendingAddConfirm")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("successful status for non-delete received -> clear mapflag_pendingAddConfirm"
); }
;
1944 (*pos).mapflags &= ~mapflag_pendingAddConfirm0x00000010;
1945 (*pos).changed = true; // this MUST be made persistent!
1946 }
1947 } // if client
1948 }
1949 else {
1950 PDEBUGPRINTFX(DBG_ERROR+DBG_EXOTIC,("dsConfirmItemOp - INTERNAL ERROR: no map entry exists for item")){ if (((0x00000002 +0x80000000) & getDbgMask()) == (0x00000002
+0x80000000)) getDbgLogger()->setNextMask(0x00000002 +0x80000000
).DebugPrintfLastMask ("dsConfirmItemOp - INTERNAL ERROR: no map entry exists for item"
); }
;
1951 }
1952 }
1953 // let inherited know as well
1954 inherited::dsConfirmItemOp(aSyncOp, aLocalID, aRemoteID, aSuccess, aErrorStatus);
1955} // TCustomImplDS::confirmItemOp
1956
1957
1958// called to mark an already sent item as "to-be-resent", e.g. due to temporary
1959// error status conditions, by localID or remoteID (latter only in server case).
1960void TCustomImplDS::implMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID)
1961{
1962 #ifdef BASED_ON_BINFILE_CLIENT1
1963 // let binfile handle it if it is active
1964 if (binfileDSActive()) {
1965 inherited::implMarkItemForResend(aLocalID, aRemoteID);
1966 return;
1967 }
1968 #endif // BASED_ON_BINFILE_CLIENT
1969
1970 // Note: this is only relevant for replaces and some adds:
1971 // - some adds will not have a map entry yet
1972 // - deletes will not have their map entry deleted until they are confirmed
1973 // But replaces would not get re-sent as they would not get detected
1974 // modified any more once a session has completed.
1975 TMapContainer::iterator pos;
1976 if (aLocalID && *aLocalID)
1977 pos=findMapByLocalID(aLocalID,mapentry_normal,false); // only undeleted ones
1978 else if (aRemoteID && *aRemoteID)
1979 pos=findMapByRemoteID(aRemoteID);
1980 else
1981 return; // neither local nor remote ID specified -> nop
1982 if (pos==fMapTable.end()) {
1983 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("implMarkItemForResend: no map entry found -> item does not exist or is an add which will be sent anyway")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("implMarkItemForResend: no map entry found -> item does not exist or is an add which will be sent anyway"
); }
;
1984 return; // no item can be searched or created
1985 }
1986 // set resend flag now (if not already set)
1987 if (!((*pos).mapflags & mapflag_resend0x00000020)) {
1988 (*pos).mapflags |= mapflag_resend0x00000020;
1989 (*pos).changed=true;
1990 }
1991 // and make sure it gets resent in case of a suspend as well (mark for resume)
1992 // This is needed for suspends where unprocessed items gets statused with 514 BEFORE
1993 // the server knows that this is going to be a suspend.
1994 (*pos).markforresume=true;
1995 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC+DBG_HOT,({ if (((0x00000040 +0x80000000 +0x00000001) & getDbgMask(
)) == (0x00000040 +0x80000000 +0x00000001)) getDbgLogger()->
setNextMask(0x00000040 +0x80000000 +0x00000001).DebugPrintfLastMask
( "localID='%s' marked for resending by setting mapflag_resend (AND mark for eventual resume!), flags now=0x%lX"
, (*pos).localid.c_str(), (long)(*pos).mapflags ); }
1996 "localID='%s' marked for resending by setting mapflag_resend (AND mark for eventual resume!), flags now=0x%lX",{ if (((0x00000040 +0x80000000 +0x00000001) & getDbgMask(
)) == (0x00000040 +0x80000000 +0x00000001)) getDbgLogger()->
setNextMask(0x00000040 +0x80000000 +0x00000001).DebugPrintfLastMask
( "localID='%s' marked for resending by setting mapflag_resend (AND mark for eventual resume!), flags now=0x%lX"
, (*pos).localid.c_str(), (long)(*pos).mapflags ); }
1997 (*pos).localid.c_str(),{ if (((0x00000040 +0x80000000 +0x00000001) & getDbgMask(
)) == (0x00000040 +0x80000000 +0x00000001)) getDbgLogger()->
setNextMask(0x00000040 +0x80000000 +0x00000001).DebugPrintfLastMask
( "localID='%s' marked for resending by setting mapflag_resend (AND mark for eventual resume!), flags now=0x%lX"
, (*pos).localid.c_str(), (long)(*pos).mapflags ); }
1998 (long)(*pos).mapflags{ if (((0x00000040 +0x80000000 +0x00000001) & getDbgMask(
)) == (0x00000040 +0x80000000 +0x00000001)) getDbgLogger()->
setNextMask(0x00000040 +0x80000000 +0x00000001).DebugPrintfLastMask
( "localID='%s' marked for resending by setting mapflag_resend (AND mark for eventual resume!), flags now=0x%lX"
, (*pos).localid.c_str(), (long)(*pos).mapflags ); }
1999 )){ if (((0x00000040 +0x80000000 +0x00000001) & getDbgMask(
)) == (0x00000040 +0x80000000 +0x00000001)) getDbgLogger()->
setNextMask(0x00000040 +0x80000000 +0x00000001).DebugPrintfLastMask
( "localID='%s' marked for resending by setting mapflag_resend (AND mark for eventual resume!), flags now=0x%lX"
, (*pos).localid.c_str(), (long)(*pos).mapflags ); }
;
2000} // TCustomImplDS::implMarkItemForResend
2001
2002
2003// called to mark an already generated (but unsent or sent but not yet statused) item
2004// as "to-be-resumed", by localID or remoteID (latter only in server case).
2005void TCustomImplDS::implMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent)
2006{
2007 #ifdef BASED_ON_BINFILE_CLIENT1
2008 // let binfile handle it if it is active
2009 if (binfileDSActive()) {
2010 inherited::implMarkItemForResume(aLocalID, aRemoteID, aUnSent);
2011 return;
2012 }
2013 #endif // BASED_ON_BINFILE_CLIENT
2014
2015 TMapContainer::iterator pos;
2016 if (aLocalID && *aLocalID)
2017 pos=findMapByLocalID(aLocalID,mapentry_normal,true); // also find deleted ones
2018 else if (aRemoteID && *aRemoteID)
2019 pos=findMapByRemoteID(aRemoteID);
2020 else
2021 return; // no item can be searched or created
2022 if (pos!=fMapTable.end()) {
2023 // we have an entry for this item, mark it for resume
2024 if ((*pos).deleted) {
2025 // undelete (re-use existing, but currently invalid entry)
2026 (*pos).remoteid.erase();
2027 (*pos).changed=true;
2028 (*pos).deleted=false;
2029 (*pos).mapflags=0;
2030 }
2031 // for unsent ones, the status is not pending any more
2032 else if (aUnSent && ((*pos).mapflags & (mapflag_pendingStatus0x00000004+mapflag_pendingDeleteStatus0x00000008))) {
2033 // reset the status pending flags
2034 (*pos).changed=true;
2035 (*pos).mapflags &= ~(mapflag_pendingStatus0x00000004+mapflag_pendingDeleteStatus0x00000008);
2036 }
2037 // For Server: those that have mapflag_pendingAddConfirm (adds) may be marked for
2038 // resume ONLY if we can rely on early maps or if they are completely unsent.
2039 // Sent adds will just keep their mapflag_pendingAddConfirm until they receive their map
2040 // For Client: all items will be marked for resume
2041 if (
2042 IS_SERVER(getSyncAppBase()->isServer()) &&
2043 ((*pos).mapflags & mapflag_pendingAddConfirm0x00000010) && // is an add...
2044 !aUnSent && // ...and already sent out
2045 !fSessionP->getSessionConfig()->fRelyOnEarlyMaps // and we can't rely on the client sending the maps before
2046 ) {
2047 // for server: already sent adds may not be repeated unless we can rely on early maps
2048 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was probably executed at remote -> NOT marked for resume"
, (*pos).localid.c_str(), (long)(*pos).mapflags ); }
2049 "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was probably executed at remote -> NOT marked for resume",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was probably executed at remote -> NOT marked for resume"
, (*pos).localid.c_str(), (long)(*pos).mapflags ); }
2050 (*pos).localid.c_str(),{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was probably executed at remote -> NOT marked for resume"
, (*pos).localid.c_str(), (long)(*pos).mapflags ); }
2051 (long)(*pos).mapflags{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was probably executed at remote -> NOT marked for resume"
, (*pos).localid.c_str(), (long)(*pos).mapflags ); }
2052 )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was probably executed at remote -> NOT marked for resume"
, (*pos).localid.c_str(), (long)(*pos).mapflags ); }
;
2053 (*pos).markforresume=false;
2054 }
2055 else {
2056 // for client: everything may be repeated and therefore marked for resume
2057 // for server: unsent adds will also be marked, or all if we can rely on early maps (which is the default)
2058 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was %s executed at remote%s -> mark for resume"
, (*pos).localid.c_str(), (long)(*pos).mapflags, aUnSent ? "NOT"
: "probably", fSessionP->getSessionConfig()->fRelyOnEarlyMaps
? " (relying on early maps)" : "" ); }
2059 "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was %s executed at remote%s -> mark for resume",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was %s executed at remote%s -> mark for resume"
, (*pos).localid.c_str(), (long)(*pos).mapflags, aUnSent ? "NOT"
: "probably", fSessionP->getSessionConfig()->fRelyOnEarlyMaps
? " (relying on early maps)" : "" ); }
2060 (*pos).localid.c_str(),{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was %s executed at remote%s -> mark for resume"
, (*pos).localid.c_str(), (long)(*pos).mapflags, aUnSent ? "NOT"
: "probably", fSessionP->getSessionConfig()->fRelyOnEarlyMaps
? " (relying on early maps)" : "" ); }
2061 (long)(*pos).mapflags,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was %s executed at remote%s -> mark for resume"
, (*pos).localid.c_str(), (long)(*pos).mapflags, aUnSent ? "NOT"
: "probably", fSessionP->getSessionConfig()->fRelyOnEarlyMaps
? " (relying on early maps)" : "" ); }
2062 aUnSent ? "NOT" : "probably",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was %s executed at remote%s -> mark for resume"
, (*pos).localid.c_str(), (long)(*pos).mapflags, aUnSent ? "NOT"
: "probably", fSessionP->getSessionConfig()->fRelyOnEarlyMaps
? " (relying on early maps)" : "" ); }
2063 fSessionP->getSessionConfig()->fRelyOnEarlyMaps ? " (relying on early maps)" : ""{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was %s executed at remote%s -> mark for resume"
, (*pos).localid.c_str(), (long)(*pos).mapflags, aUnSent ? "NOT"
: "probably", fSessionP->getSessionConfig()->fRelyOnEarlyMaps
? " (relying on early maps)" : "" ); }
2064 )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was %s executed at remote%s -> mark for resume"
, (*pos).localid.c_str(), (long)(*pos).mapflags, aUnSent ? "NOT"
: "probably", fSessionP->getSessionConfig()->fRelyOnEarlyMaps
? " (relying on early maps)" : "" ); }
;
2065 (*pos).markforresume=true;
2066 }
2067 }
2068 else if (aLocalID && *aLocalID) {
2069 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', was not yet in map and was %sexecuted at remote -> created map and marked for resume"
, aLocalID, aUnSent ? "NOT " : "probably" ); }
2070 "implMarkItemForResume: localID='%s', was not yet in map and was %sexecuted at remote -> created map and marked for resume",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', was not yet in map and was %sexecuted at remote -> created map and marked for resume"
, aLocalID, aUnSent ? "NOT " : "probably" ); }
2071 aLocalID,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', was not yet in map and was %sexecuted at remote -> created map and marked for resume"
, aLocalID, aUnSent ? "NOT " : "probably" ); }
2072 aUnSent ? "NOT " : "probably"{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', was not yet in map and was %sexecuted at remote -> created map and marked for resume"
, aLocalID, aUnSent ? "NOT " : "probably" ); }
2073 )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "implMarkItemForResume: localID='%s', was not yet in map and was %sexecuted at remote -> created map and marked for resume"
, aLocalID, aUnSent ? "NOT " : "probably" ); }
;
2074 // we have no entry for this item, make one (only if this is not a remoteID-only item,
2075 // but this should not occur here anyway - items without localID can only be replaces
2076 // from server to client, in which case we have a map entry anyway)
2077 TMapEntry entry;
2078 entry.entrytype=mapentry_normal;
2079 entry.localid=aLocalID;
2080 entry.remoteid.erase();
2081 entry.mapflags=0;
2082 entry.added=true;
2083 entry.changed=true;
2084 entry.deleted=false;
2085 entry.markforresume=true;
2086 entry.savedmark=false;
2087 fMapTable.push_back(entry);
2088 }
2089} // TCustomImplDS::implMarkItemForResume
2090
2091
2092
2093// Get next item from database
2094localstatus TCustomImplDS::implGetItem(
2095 bool &aEof,
2096 bool &aChanged, // if set on entry, only changed ones will be reported, otherwise all will be returned and aChanged contains flag if entry has changed or not
2097 TSyncItem* &aSyncItemP
2098)
2099{
2100 #ifdef BASED_ON_BINFILE_CLIENT1
2101 // let binfile handle it if it is active
2102 if (binfileDSActive()) {
2103 return inherited::implGetItem(aEof, aChanged, aSyncItemP);
2104 }
2105 #endif // BASED_ON_BINFILE_CLIENT
2106
2107 localstatus sta = LOCERR_OK;
2108 bool reportChangedOnly = aChanged; // save initial state, as we might repeat...
2109 bool rep=true; // to start-up lower part
2110 TSyncOperation sop=sop_none;
2111 aEof=true; // assume we have nothing to report
2112 string remid;
2113 TMultiFieldItem *myitemP=NULL__null;
2114
2115 // short-cut if refreshing only and not slowsync resuming (then we need the items for comparison)
2116 if (isRefreshOnly() && !isCacheData() && !(isResuming() && isSlowSync()))
2117 return sta; // aEof is set, return nothing
2118
2119 TP_DEFIDX(li);
2120 TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
2121 SYSYNC_TRYtry {
2122 // check mode
2123 if (fGetPhase==gph_deleted) {
2124 // report deleted items, that is those in the map that are not
2125 // any more in the sync set of local IDs
2126 // Note: we need no extra database access any more for this
2127 if (!fGetPhasePrepared) {
2128 // start at beginning of map table
2129 fDeleteMapPos=fMapTable.begin();
2130 fGetPhasePrepared=true;
2131 }
2132 do {
2133 rep=false;
2134 // report all those map entries that have no data entry (any more)
2135 // as deleted records
2136 // - check if there is more data in map table to process
2137 if (fDeleteMapPos==fMapTable.end()) {
2138 fGetPhase=gph_added_changed; // end of this phase, now report additions and modifications
2139 rep=true; // continue in second part (get updated records)
2140 fGetPhasePrepared=false; // next phase must be prepared
2141 break;
2142 }
2143 else {
2144 // search if there is a local ID still existing for that ID
2145 TMapEntry entry = (*fDeleteMapPos);
2146 // check only undeleted map entries
2147 if (entry.deleted || entry.entrytype!=mapentry_normal) {
2148 // deleted or non-normal map entry - simply skip
2149 rep=true;
2150 }
2151 else if (isResuming() && !(entry.mapflags & mapflag_useforresume0x00000001)) {
2152 // this delete has already been reported deleted in previous suspended session
2153 // (or it was deleted from the datastore WHILE or after start of a suspended session,
2154 // in this case the item will be reported deleted in the NEXT complete sync session)
2155 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Resuming and found item not marked for resume -> ignore for delete checking")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Resuming and found item not marked for resume -> ignore for delete checking"
); }
;
2156 rep=true;
2157 }
2158 else if (findInSyncSet(entry.localid.c_str())==fSyncSetList.end()) {
2159 // this item has been deleted (and not yet been reported if this is a resume) -> report it
2160 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Normal sync and found item in map which is not in syncset -> delete and mark with mapflag_pendingDeleteStatus")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Normal sync and found item in map which is not in syncset -> delete and mark with mapflag_pendingDeleteStatus"
); }
;
2161 // - create new empty TMultiFieldItem
2162 myitemP =
2163 (TMultiFieldItem *) newItemForRemote(ity_multifield);
2164 // - add IDs
2165 myitemP->setRemoteID(entry.remoteid.c_str());
2166 myitemP->setLocalID(entry.localid.c_str());
2167 // - set operation
2168 myitemP->setSyncOp(sop_delete);
2169 // - set item
2170 aSyncItemP = myitemP;
2171 aEof=false; // report something
2172 // mark entry as waiting for delete status
2173 // NOTES: - we cannot delete the map entry until we get a confirmItemOp() for it
2174 // - the pendingStatus flag does not need to be persistent between sessions, so we don't set the changed flag here!
2175 // - the flag is important in case a server-delete vs client-replace conflict occurs which the client wins. In that
2176 // case implProcessItem needs to be able to tell that still having a map entry does NOT mean we do have
2177 // the record still in the DB.
2178 entry.mapflags |= mapflag_pendingDeleteStatus0x00000008;
2179 (*fDeleteMapPos)=entry; // save updated entry in list
2180 // found one to report
2181 }
2182 else {
2183 rep=true; // must repeat again
2184 }
2185 }
2186 // go to next
2187 fDeleteMapPos++;
2188 } while(rep);
2189 } // report deleted phase
2190 if (rep && fGetPhase==gph_added_changed) {
2191 // report changed and added items. Those that are in the sync set but
2192 // not yet in the map are added items, those that are already in the
2193 // map are changed items if the mod date is newer than last sync
2194 // and deleted if they don't pass extra filters
2195 if (!fGetPhasePrepared) {
2196 // start at beginning of map table
2197 fSyncSetPos=fSyncSetList.begin();
2198 fGetPhasePrepared=true;
2199 }
2200 do {
2201 rep=false;
2202 remid.erase();
2203 // - check if there is more data in the syncset to process
2204 if (fSyncSetPos==fSyncSetList.end()) {
2205 fGetPhase=gph_done; // end of this phase, now report additions and modifications
2206 rep=true; // continue in next phase (if any)
2207 fGetPhasePrepared=false; // next phase must be prepared
2208 break;
2209 }
2210 else {
2211 // get syncset entry
2212 TSyncSetItem *syncsetitemP = (*fSyncSetPos);
2213 sop=sop_none;
2214 TMapContainer::iterator pos;
2215 // search if there is a map entry for this item already
2216 pos=findMapByLocalID(syncsetitemP->localid.c_str(),mapentry_normal); // do not find deleted ones, only valid entries!
2217 #ifdef SYDEBUG2
2218 if (pos!=fMapTable.end()) {
2219 // Debug
2220 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, syncsetitemP->localid.c_str(), (*pos).remoteid.c_str(), (
long)(*pos).mapflags, (int)(*pos).changed, (int)(*pos).deleted
, (int)(*pos).added, (int)(*pos).markforresume, (int)(*pos).savedmark
); }
2221 "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, syncsetitemP->localid.c_str(), (*pos).remoteid.c_str(), (
long)(*pos).mapflags, (int)(*pos).changed, (int)(*pos).deleted
, (int)(*pos).added, (int)(*pos).markforresume, (int)(*pos).savedmark
); }
2222 syncsetitemP->localid.c_str(),{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, syncsetitemP->localid.c_str(), (*pos).remoteid.c_str(), (
long)(*pos).mapflags, (int)(*pos).changed, (int)(*pos).deleted
, (int)(*pos).added, (int)(*pos).markforresume, (int)(*pos).savedmark
); }
2223 (*pos).remoteid.c_str(),{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, syncsetitemP->localid.c_str(), (*pos).remoteid.c_str(), (
long)(*pos).mapflags, (int)(*pos).changed, (int)(*pos).deleted
, (int)(*pos).added, (int)(*pos).markforresume, (int)(*pos).savedmark
); }
2224 (long)(*pos).mapflags,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, syncsetitemP->localid.c_str(), (*pos).remoteid.c_str(), (
long)(*pos).mapflags, (int)(*pos).changed, (int)(*pos).deleted
, (int)(*pos).added, (int)(*pos).markforresume, (int)(*pos).savedmark
); }
2225 (int)(*pos).changed,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, syncsetitemP->localid.c_str(), (*pos).remoteid.c_str(), (
long)(*pos).mapflags, (int)(*pos).changed, (int)(*pos).deleted
, (int)(*pos).added, (int)(*pos).markforresume, (int)(*pos).savedmark
); }
2226 (int)(*pos).deleted,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, syncsetitemP->localid.c_str(), (*pos).remoteid.c_str(), (
long)(*pos).mapflags, (int)(*pos).changed, (int)(*pos).deleted
, (int)(*pos).added, (int)(*pos).markforresume, (int)(*pos).savedmark
); }
2227 (int)(*pos).added,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, syncsetitemP->localid.c_str(), (*pos).remoteid.c_str(), (
long)(*pos).mapflags, (int)(*pos).changed, (int)(*pos).deleted
, (int)(*pos).added, (int)(*pos).markforresume, (int)(*pos).savedmark
); }
2228 (int)(*pos).markforresume,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, syncsetitemP->localid.c_str(), (*pos).remoteid.c_str(), (
long)(*pos).mapflags, (int)(*pos).changed, (int)(*pos).deleted
, (int)(*pos).added, (int)(*pos).markforresume, (int)(*pos).savedmark
); }
2229 (int)(*pos).savedmark{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, syncsetitemP->localid.c_str(), (*pos).remoteid.c_str(), (
long)(*pos).mapflags, (int)(*pos).changed, (int)(*pos).deleted
, (int)(*pos).added, (int)(*pos).markforresume, (int)(*pos).savedmark
); }
2230 )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ( "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d"
, syncsetitemP->localid.c_str(), (*pos).remoteid.c_str(), (
long)(*pos).mapflags, (int)(*pos).changed, (int)(*pos).deleted
, (int)(*pos).added, (int)(*pos).markforresume, (int)(*pos).savedmark
); }
;
2231 }
2232 #endif
2233 // now find what syncop results
2234 if (fSlowSync && !isResuming()) {
2235 // all items in local sync set are to be reported
2236 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Slow sync and not resuming -> all items are first reported sop_wants_replace (will become add later)")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Slow sync and not resuming -> all items are first reported sop_wants_replace (will become add later)"
); }
;
2237 sop=sop_wants_replace;
2238 aChanged=true;
2239 // clear the resend flags if any
2240 if (pos!=fMapTable.end()) {
2241 if ((*pos).mapflags & mapflag_resend0x00000020) {
2242 (*pos).mapflags &= ~mapflag_resend0x00000020;
2243 (*pos).changed = true;
2244 }
2245 }
2246 }
2247 else {
2248 if (pos!=fMapTable.end()) {
2249 // for slowsync resume - items that are not marked for resume, but already have a remoteID mapped
2250 // must be presented for re-match with sop_reference_only
2251 if (IS_SERVER(getSyncAppBase()->isServer()) && fSlowSync && isResuming() && !((*pos).mapflags & mapflag_useforresume0x00000001) && !(*pos).remoteid.empty()) {
2252 // this item apparently was already slow-sync-matched before the suspend - still show it for reference to avoid re-adding it
2253 sop=sop_reference_only;
2254 }
2255 else if (!isRefreshOnly() || (isRefreshOnly() && isCacheData())) {
2256 // item is already in map: check if this is an already detected, but unfinished add
2257 if (!((*pos).mapflags & mapflag_pendingAddConfirm0x00000010)) {
2258 // is a replace (not an add): changed if mod date newer or resend flagged (AND updates enabled)
2259 // Note: For reporting modifications, the date of last sending-data-to-remote date is relevant
2260 bool hasChanged=
2261 (((*fSyncSetPos)->isModified) || ((*pos).mapflags & mapflag_resend0x00000020))
2262 && fReportUpdates;
2263 // reset resend flag here if it acutually causes a resend here (otherwise, keep it for later)
2264 if (hasChanged && (*pos).mapflags & mapflag_resend0x00000020) {
2265 PDEBUGPRINTFX(DBG_ADMIN+DBG_HOT,("Item '%s' treated as changed because resend-flag was set",syncsetitemP->localid.c_str())){ if (((0x00000040 +0x00000001) & getDbgMask()) == (0x00000040
+0x00000001)) getDbgLogger()->setNextMask(0x00000040 +0x00000001
).DebugPrintfLastMask ("Item '%s' treated as changed because resend-flag was set"
,syncsetitemP->localid.c_str()); }
;
2266 (*pos).mapflags &= ~mapflag_resend0x00000020;
2267 (*pos).changed = true;
2268 }
2269 if (isResuming()) {
2270 // Basically, on resume just report those that have the mapflag_useforresume flag set.
2271 // However, there is one difficult problem here:
2272 // - Those that were successfully modified in the suspended part of a session, but get modified
2273 // AGAIN between suspend and this resume will NOT be detected as changes any more. Therefore, for
2274 // items we see here that don't have the mapflag_useforresume, we need to check additionally if
2275 // they possibly have changed AFTER THE LAST SUSPEND SAVE
2276 if ((*pos).mapflags & mapflag_useforresume0x00000001) {
2277 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Resuming and found marked-for-resume -> send replace")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Resuming and found marked-for-resume -> send replace"
); }
;
2278 sop=sop_wants_replace;
2279 hasChanged=true; // mark this as change (again, because marked for resume)
2280 }
2281 else if (!reportChangedOnly || hasChanged) {
2282 // this one does not have the flag set, but it would be reported as a change if this was not a resume
2283 // so this could be a change happened between suspend and resume
2284 if (syncsetitemP->isModifiedAfterSuspend) {
2285 // yes, this one was modified AFTER the suspended session, so we'll send it, too
2286 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Resuming and found NOT marked-for-resume, but changed after last suspend -> send replace again")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Resuming and found NOT marked-for-resume, but changed after last suspend -> send replace again"
); }
;
2287 sop=sop_wants_replace;
2288 hasChanged=true; // mark this as change (since last suspend, that is)
2289 }
2290 }
2291 }
2292 else if (!reportChangedOnly || hasChanged) {
2293 // report only if aChanged was false on entry (=reportChangedOnly is false) or if modified anyway
2294 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Normal sync, item changed -> send replace")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Normal sync, item changed -> send replace"
); }
;
2295 sop=sop_wants_replace;
2296 }
2297 remid=(*pos).remoteid;
2298 aChanged=hasChanged; // return changed status anyway
2299 } // if not add
2300 else {
2301 // already detected, but unfinished add
2302 // For server: Might be resent ONLY if resuming and marked for resume (otherwise, we MUST wait for map or we'll get duplicates)
2303 // For client: resend always except if resuming and not marked for it
2304 if (isResuming()) {
2305 if ((*pos).mapflags & mapflag_useforresume0x00000001) {
2306 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Resuming and found marked-for-resume with mapflag_pendingAddConfirm -> unsent add, send it again")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Resuming and found marked-for-resume with mapflag_pendingAddConfirm -> unsent add, send it again"
); }
;
2307 sop=sop_wants_add;
2308 }
2309 else {
2310 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Resuming and NOT marked-for-resume with mapflag_pendingAddConfirm -> ignore")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Resuming and NOT marked-for-resume with mapflag_pendingAddConfirm -> ignore"
); }
;
2311 }
2312 }
2313 else {
2314 if (IS_CLIENT(!getSyncAppBase()->isServer())) {
2315 // for client - repeating an add does not harm (but helps if it did not reach the server in the previous attempt
2316 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Non-resume sync found item with mapflag_pendingAddConfirm -> send it again")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Non-resume sync found item with mapflag_pendingAddConfirm -> send it again"
); }
;
2317 sop=sop_wants_add;
2318 } // client
2319 else {
2320 // for server - repeating an add potentially DOES harm (duplicate if client already got the add, but didn't send a map yet)
2321 // but it's ok if it's flagged as an explicit resend (this happens only if we have got error status from remote)
2322 if ((*pos).mapflags & mapflag_resend0x00000020) {
2323 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Item with mapflag_pendingAddConfirm (add) also has mapflag_resend -> we can safely resend")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Item with mapflag_pendingAddConfirm (add) also has mapflag_resend -> we can safely resend"
); }
;
2324 sop=sop_wants_add;
2325 // - reset resend flag here
2326 (*pos).mapflags &= ~mapflag_resend0x00000020;
2327 (*pos).changed = true;
2328 }
2329 else {
2330 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Non-resume sync found item with mapflag_pendingAddConfirm (add) -> ignore until map is found")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Non-resume sync found item with mapflag_pendingAddConfirm (add) -> ignore until map is found"
); }
;
2331 }
2332 } // server
2333 }
2334 }
2335 } // if not refreshonly
2336 } // a map entry already exists
2337 else {
2338 // item is not yet in map: this is a new one. Report it if we are not resuming a previous session
2339 // (in this case, items added after start of the original session will be detected at NEXT full
2340 // session, so just leave it out for now)
2341 // Note: this is first-time add detection. If we get this reported as sop_wants_add below, a map with
2342 // mapflag_pendingAddConfirm will be created for it.
2343 if (isRefreshOnly() && isCacheData()) {
2344 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("New item (no map yet) detected during Refresh only -> ignore for now, will be deleted later unless matched against peer item")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("New item (no map yet) detected during Refresh only -> ignore for now, will be deleted later unless matched against peer item"
); }
;
2345 sop=sop_none;
2346 } else if (isRefreshOnly()) {
2347 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("New item (no map yet) detected during Refresh only -> ignore for now, will be added in next two-way sync")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("New item (no map yet) detected during Refresh only -> ignore for now, will be added in next two-way sync"
); }
;
2348 sop=sop_none;
2349 }
2350 else if (isResuming()) {
2351 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Resuming and found new item which was not present in original session -> ignore for now, will be added in next regular sync")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Resuming and found new item which was not present in original session -> ignore for now, will be added in next regular sync"
); }
;
2352 sop=sop_none;
2353 }
2354 else {
2355 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Normal sync, item not yet in map -> add to remote")){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Normal sync, item not yet in map -> add to remote"
); }
;
2356 sop=sop_wants_add;
2357 }
2358 }
2359 }
2360 if (sop!=sop_none) {
2361 // we need the item from the database
2362 SYSYNC_TRYtry {
2363 bool fetched=false;
2364 // - check if we've already read it (at apiReadSyncSet())
2365 if (syncsetitemP->itemP) {
2366 // yes, take it out of the syncset
2367 if (fNoSingleItemRead) {
2368 // we may need it later, so we can only pass a copy to caller
2369 myitemP =
2370 (TMultiFieldItem *) newItemForRemote(ity_multifield);
2371 if (!myitemP)
2372 SYSYNC_THROW(TSyncException("newItemForRemote could not create new Item"))throw TSyncException("newItemForRemote could not create new Item"
)
;
2373 // copy item (id, op, contents)
2374 (*myitemP) = (*(syncsetitemP->itemP));
2375 }
2376 else {
2377 // we don't need it later, pass original to caller and remove link in syncsetitem
2378 myitemP = syncsetitemP->itemP;
2379 syncsetitemP->itemP = NULL__null; // syncsetitem does not own it any longer
2380 }
2381 fetched=true; // we have the item
2382 // Make sure item is fully equipped
2383 // - assign local id, as it is required by DoDataSubstitutions
2384 myitemP->setLocalID(syncsetitemP->localid.c_str());
2385 // - assign remote id if we know one
2386 myitemP->setRemoteID(remid.c_str());
2387 // - set operation
2388 myitemP->setSyncOp(sop);
2389 }
2390 else {
2391 // We need to read the item here
2392 // - create new empty TMultiFieldItem
2393 myitemP =
2394 (TMultiFieldItem *) newItemForRemote(ity_multifield);
2395 if (!myitemP)
2396 SYSYNC_THROW(TSyncException("newItemForRemote could not create new Item"))throw TSyncException("newItemForRemote could not create new Item"
)
;
2397 // - assign local id, as it is required by DoDataSubstitutions
2398 myitemP->setLocalID(syncsetitemP->localid.c_str());
2399 // - assign remote id if we know one
2400 myitemP->setRemoteID(remid.c_str());
2401 // - set operation
2402 myitemP->setSyncOp(sop);
2403 // Now fetch item (read phase)
2404 sta=apiFetchItem(*myitemP,true,syncsetitemP);
2405 if (sta==LOCERR_OK) {
2406 // successfully fetched
2407 fetched=true;
2408 }
2409 else if (sta==404) {
2410 // this record has been deleted since we have read the
2411 // localid list. If this is was an add, we can simply
2412 // ignore it
2413 // - decide what this means now
2414 if (sop==sop_reference_only || sop==sop_wants_add) {
2415 // was deleted before we could fetch it for adding (or reference), just ignore
2416 PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,("to-be-added record localID=%s was deleted during this sync session -> ignore",myitemP->getLocalID())){ if (((0x00000080 +0x40000000) & getDbgMask()) == (0x00000080
+0x40000000)) getDbgLogger()->setNextMask(0x00000080 +0x40000000
).DebugPrintfLastMask ("to-be-added record localID=%s was deleted during this sync session -> ignore"
,myitemP->getLocalID()); }
;
2417 rep=true;
2418 delete myitemP;
2419 // could still be that we have a mapflag_useforresume map entry, make sure we get rid of it
2420 if (pos!=fMapTable.end()) {
2421 // mark deleted
2422 (*pos).deleted=true;
2423 }
2424 goto nextchanged;
2425 }
2426 else {
2427 // was changed, but now doesn't exist any more -> treat as delete
2428 // - adjust operation
2429 PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,("to-be-changed record localID=%s was deleted during this sync session -> delete",myitemP->getLocalID())){ if (((0x00000080 +0x40000000) & getDbgMask()) == (0x00000080
+0x40000000)) getDbgLogger()->setNextMask(0x00000080 +0x40000000
).DebugPrintfLastMask ("to-be-changed record localID=%s was deleted during this sync session -> delete"
,myitemP->getLocalID()); }
;
2430 myitemP->setSyncOp(sop_delete);
2431 myitemP->cleardata(); // make sure it is empty
2432 // - set item to return to caller
2433 aSyncItemP = myitemP;
2434 aEof=false; // report something
2435 if (!fSlowSync) {
2436 // Note: we can safely assume that the map entry exists - otherwise sop would be sop_wants_add
2437 // map entry must be marked to show that this now is a pending delete
2438 (*pos).changed=true;
2439 (*pos).mapflags &= ~(mapflag_pendingStatus0x00000004);
2440 (*pos).mapflags |= mapflag_pendingDeleteStatus0x00000008;
2441 }
2442 fetched=false; // prevent map manipulations below
2443 }
2444 }
2445 else {
2446 // other error
2447 SYSYNC_THROW(TSyncException("Error fetching data from DB",sta))throw TSyncException("Error fetching data from DB",sta);
2448 }
2449 } // else: fetch from DB needed
2450 if (fetched) {
2451 // set item to return to caller
2452 aSyncItemP = myitemP;
2453 aEof=false; // report something
2454 // info
2455 PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,("Fetched record data from DB with localID=%s",myitemP->getLocalID())){ if (((0x00000080 +0x40000000) & getDbgMask()) == (0x00000080
+0x40000000)) getDbgLogger()->setNextMask(0x00000080 +0x40000000
).DebugPrintfLastMask ("Fetched record data from DB with localID=%s"
,myitemP->getLocalID()); }
;
2456 }
2457 }
2458 SYSYNC_CATCH(...)catch(...) {
2459 if (myitemP) delete myitemP;
2460 SYSYNC_RETHROWthrow;
2461 SYSYNC_ENDCATCH}
2462 } // item to report
2463 else {
2464 // item read must not be reported, try to get next
2465 rep=true;
2466 }
2467 } // not end of syncset
2468 nextchanged:
2469 // go to next
2470 fSyncSetPos++;
2471 } while(rep);
2472 } // report added and changed phase
2473 if (rep) {
2474 // end of items
2475 aEof=true;
2476 // syncset can be deleted only if we can retrieve individual items later from DB
2477 // if not, we must keep the syncset in memory
2478 if (!fNoSingleItemRead) {
2479 // we don't need the SyncSet list any more
2480 // (and especially the items that did NOT get reported to the caller of GetItem
2481 // can be deleted now to free memory. Reported items are now owned by the caller)
2482 DeleteSyncSet(fMultiFolderDB);
2483 }
2484 }
2485 // done
2486 TP_START(fSessionP->fTPInfo,li);
2487 // show item fetched
2488 #ifdef SYDEBUG2
2489 if (PDEBUGTEST(DBG_DATA+DBG_SCRIPTS)(((0x00000080 +0x00001000) & getDbgMask()) == (0x00000080
+0x00001000))
&& aSyncItemP)
2490 aSyncItemP->debugShowItem(DBG_DATA0x00000080); // show item fetched
2491 #endif
2492 }
2493 SYSYNC_CATCH(exception &e)catch(exception &e) {
2494 PDEBUGPRINTFX(DBG_ERROR,("GetItem exception: %s",e.what())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("GetItem exception: %s"
,e.what()); }
;
2495 TP_START(fSessionP->fTPInfo,li);
2496 sta=510;
2497 SYSYNC_ENDCATCH}
2498 // done
2499 return sta;
2500} // TCustomImplDS::implGetItem
2501
2502#endif // not BINFILE_ALWAYS_ACTIVE
2503
2504
2505// end of read
2506localstatus TCustomImplDS::implEndDataRead(void)
2507{
2508 #ifdef BASED_ON_BINFILE_CLIENT1
2509 // let binfile handle it if it is active
2510 if (binfileDSActive()) {
2511 return inherited::implEndDataRead();
2512 }
2513 #endif // BASED_ON_BINFILE_CLIENT
2514 // let API handle it directly
2515 return apiEndDataRead();
2516} // TCustomImplDS::implEndDataRead
2517
2518
2519// start of write
2520localstatus TCustomImplDS::implStartDataWrite()
2521{
2522 localstatus sta = LOCERR_OK;
2523
2524 #ifdef BASED_ON_BINFILE_CLIENT1
2525 // let binfile handle it if it is active
2526 if (binfileDSActive()) {
2527 sta = inherited::implStartDataWrite();
2528 }
2529 else
2530 #endif // BASED_ON_BINFILE_CLIENT
2531 {
2532 #ifndef BINFILE_ALWAYS_ACTIVE
2533 SYSYNC_TRYtry {
2534 // let actual data implementation prepare
2535 sta = apiStartDataWrite();
2536 if (sta==LOCERR_OK) {
2537 // Notes:
2538 // - transaction starts implicitly when first INSERT / UPDATE / DELETE occurs
2539 // - resumed slow refreshes must NOT zap the sync set again!
2540 // - prevent zapping when datastore is in readonly mode!
2541 if (fRefreshOnly && !fCacheData && fSlowSync && !isResuming() && !fReadOnly) {
2542 // - make sure we have at least one pev_deleting event, in case app tracks it to see if session caused changes to DB
2543 DB_PROGRESS_EVENT(this,pev_deleting,0,0,0)this->getSession()->NotifySessionProgressEvent(pev_deleting
,this->getDSConfig(),0,0,0)
;
2544 // now, we need to zap the DB first
2545 PDEBUGBLOCKFMTCOLL(("ZapSyncSet","Zapping sync set in database","datastore=%s",getName()))getDbgLogger()->DebugOpenBlockCollapsed ("ZapSyncSet","Zapping sync set in database"
,"datastore=%s",getName())
;
2546 SYSYNC_TRYtry {
2547 sta=apiZapSyncSet();
2548 PDEBUGENDBLOCK("ZapSyncSet")getDbgLogger()->DebugCloseBlock( "ZapSyncSet");
2549 }
2550 SYSYNC_CATCH(exception &e)catch(exception &e) {
2551 PDEBUGPRINTFX(DBG_ERROR,("ZapSyncSet exception: %s",e.what())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("ZapSyncSet exception: %s"
,e.what()); }
;
2552 sta=510;
2553 // end of DB read
2554 PDEBUGENDBLOCK("ZapSyncSet")getDbgLogger()->DebugCloseBlock( "ZapSyncSet");
2555 SYSYNC_ENDCATCH}
2556 if (sta!=LOCERR_OK) {
2557 PDEBUGPRINTFX(DBG_ERROR,("implStartDataWrite: cannot zap data for refresh, status=%hd",sta)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("implStartDataWrite: cannot zap data for refresh, status=%hd"
,sta); }
;
2558 }
2559 // ok, now that the old data is zapped, we MUST forget the former sync set, it is now for sure invalid
2560 DeleteSyncSet(false);
2561 }
2562 }
2563 }
2564 SYSYNC_CATCH(exception &e)catch(exception &e) {
2565 PDEBUGPRINTFX(DBG_ERROR,("implStartDataWrite exception: %s",e.what())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("implStartDataWrite exception: %s"
,e.what()); }
;
2566 sta=510;
2567 SYSYNC_ENDCATCH}
2568 #endif
2569 }
2570 // done
2571 return sta;
2572} // TCustomImplDS::implStartDataWrite
2573
2574
2575#ifndef BINFILE_ALWAYS_ACTIVE
2576
2577// review reported entry (allows post-processing such as map deleting)
2578// MUST be called after StartDataWrite, before any actual writing,
2579// for each item obtained in GetItem
2580localstatus TCustomImplDS::implReviewReadItem(
2581 TSyncItem &aItem // the item
2582)
2583{
2584 #ifdef BASED_ON_BINFILE_CLIENT1
2585 // let binfile handle it if it is active
2586 if (binfileDSActive()) {
2587 return inherited::implReviewReadItem(aItem);
2588 }
2589 #endif // BASED_ON_BINFILE_CLIENT
2590
2591 // get the operation
2592 TSyncOperation sop = aItem.getSyncOp();
2593 // NOTE: Don't touch map if this is a for-reference-only (meaning that the map is
2594 // already ok, and it is included here ONLY to find possible slowsync matches)!
2595 if (sop!=sop_reference_only) {
2596 // Adjust map flags or create map if needed
2597 if (fSlowSync || sop==sop_add || sop==sop_wants_add) {
2598 // for slowsync, all items are kind of "adds", that is, not yet mapped (server case)
2599 // or not yet statused (client case)
2600 // for normal sync, make sure adds get mapflag_pendingAddConfirm set and remoteID gets cleared (is not valid in any case)
2601 modifyMap(mapentry_normal,aItem.getLocalID(),"",mapflag_pendingAddConfirm0x00000010+mapflag_pendingStatus0x00000004,false,mapflag_pendingDeleteStatus0x00000008);
2602 }
2603 else if (sop==sop_delete) {
2604 // In case when postFetch filtering changed an item from replace to delete,
2605 // we must make sure that the map entry gets the deleted status set
2606 // - simply make sure deleted item's map entry will get deleted once the delete is confirmed
2607 modifyMap(mapentry_normal,aItem.getLocalID(),NULL__null,mapflag_pendingDeleteStatus0x00000008,false,0);
2608 }
2609 else {
2610 // not add (and never a delete here) -> is replace. Set status pending flag (which doesn't need to be saved to DB)
2611 modifyMap(mapentry_normal,aItem.getLocalID(),NULL__null,mapflag_pendingStatus0x00000004,false,mapflag_pendingAddConfirm0x00000010+mapflag_pendingDeleteStatus0x00000008);
2612 }
2613 }
2614 return LOCERR_OK;
2615} // TCustomImplDS::implReviewReadItem
2616
2617
2618// - retrieve specified item from database
2619bool TCustomImplDS::implRetrieveItemByID(
2620 TSyncItem &aItem, // the item
2621 TStatusCommand &aStatusCommand
2622)
2623{
2624 #ifdef BASED_ON_BINFILE_CLIENT1
2625 // let binfile handle it if it is active
2626 if (binfileDSActive()) {
2627 return inherited::implRetrieveItemByID(aItem, aStatusCommand);
2628 }
2629 #endif // BASED_ON_BINFILE_CLIENT
2630
2631 bool ok=true;
2632 // determine item's local ID
2633 if (!aItem.hasLocalID()) {
2634 if (IS_CLIENT(!getSyncAppBase()->isServer())) {
2635 // client case: MUST have local ID
2636 aStatusCommand.setStatusCode(400); // bad request (no address)
2637 return false;
2638 }
2639 else {
2640 // no local ID specified directly, address by remote ID
2641 if (!aItem.hasRemoteID()) {
2642 aStatusCommand.setStatusCode(400); // bad request (no address)
2643 return false;
2644 }
2645 // lookup remote ID in map
2646 TMapContainer::iterator mappos = findMapByRemoteID(aItem.getRemoteID());
2647 if (mappos==fMapTable.end()) {
2648 aStatusCommand.setStatusCode(404); // not found
2649 return false;
2650 }
2651 // set local ID
2652 aItem.setLocalID(mappos->localid.c_str());
2653 // check if we have a local ID now
2654 if (!aItem.hasLocalID()) {
2655 aStatusCommand.setStatusCode(400); // bad request (no address)
2656 return false;
2657 }
2658 }
2659 }
2660 TP_DEFIDX(li);
2661 TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
2662 // if we can't fetch single items from DB, we should have all
2663 // in the syncset and can get them from there
2664 if (fNoSingleItemRead) {
2665 // search sync set by localID
2666 TSyncSetList::iterator pos=findInSyncSet(aItem.getLocalID());
2667 if (pos!=fSyncSetList.end() && (*pos)->itemP) {
2668 // found, copy data to item passed
2669 aItem.replaceDataFrom(*((*pos)->itemP));
2670 }
2671 else {
2672 // not found
2673 aStatusCommand.setStatusCode(404); // not found
2674 ok=false;
2675 }
2676 }
2677 else {
2678 // fetch from DB
2679 SYSYNC_TRYtry {
2680 // fetch data from DB
2681 localstatus sta=apiFetchItem(*((TMultiFieldItem *)&aItem),false,NULL__null); // no syncsetitem known
2682 if (sta!=LOCERR_OK) {
2683 aStatusCommand.setStatusCode(sta);
2684 ok=false;
2685 }
2686 }
2687 SYSYNC_CATCH(exception &e)catch(exception &e) {
2688 PDEBUGPRINTFX(DBG_ERROR,("implRetrieveItemByID exception: %s",e.what())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("implRetrieveItemByID exception: %s"
,e.what()); }
;
2689 aStatusCommand.setStatusCode(510);
2690 ok=false;
2691 SYSYNC_ENDCATCH}
2692 }
2693 // return status
2694 TP_START(fSessionP->fTPInfo,li);
2695 return ok;
2696} // TCustomImplDS::implRetrieveItemByID
2697
2698
2699
2700/// called to set maps.
2701/// @note aRemoteID or aLocalID can be NULL - which signifies deletion of a map entry
2702/// @note that this might be needed for clients accessing a server-style database as well
2703localstatus TCustomImplDS::implProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID)
2704{
2705 localstatus sta = 510; // error
2706 // Note: Map must be ready to have either empty local or remote ID to delete an entry
2707 if (!aLocalID) {
2708 // delete by remote ID
2709 modifyMap(mapentry_normal,NULL__null,aRemoteID,0,true);
2710 PDEBUGPRINTFX(DBG_ADMIN,("Map entry (or entries) for RemoteID='%s' removed",aRemoteID)){ if (((0x00000040) & getDbgMask()) == (0x00000040)) getDbgLogger
()->setNextMask(0x00000040).DebugPrintfLastMask ("Map entry (or entries) for RemoteID='%s' removed"
,aRemoteID); }
;
2711 sta=LOCERR_OK;
2712 }
2713 else {
2714 // - if RemoteID is empty, this means that Map should be deleted
2715 if (!aRemoteID) {
2716 // Map delete request
2717 modifyMap(mapentry_normal,aLocalID,NULL__null,0,true);
2718 PDEBUGPRINTFX(DBG_ADMIN,("Map entry (or entries) for LocalID='%s' removed",aLocalID)){ if (((0x00000040) & getDbgMask()) == (0x00000040)) getDbgLogger
()->setNextMask(0x00000040).DebugPrintfLastMask ("Map entry (or entries) for LocalID='%s' removed"
,aLocalID); }
;
2719 sta=LOCERR_OK;
2720 }
2721 else {
2722 // Map modify or add request, automatically clears mapflag_pendingAddConfirm (and all other)
2723 // flag(s), INLCUDING resume mark. So even if all sent adds (and not only the unsent ones)
2724 // get marked for resume in a suspend (fRelyOnEarlyMaps set), those that actually got added
2725 // in the previous session will not be re-sent for sure.
2726 modifyMap(mapentry_normal,aLocalID,aRemoteID,0,false);
2727 PDEBUGPRINTFX(DBG_ADMIN,("Map entry updated: LocalID='%s', RemoteID='%s'",aLocalID,aRemoteID)){ if (((0x00000040) & getDbgMask()) == (0x00000040)) getDbgLogger
()->setNextMask(0x00000040).DebugPrintfLastMask ("Map entry updated: LocalID='%s', RemoteID='%s'"
,aLocalID,aRemoteID); }
;
2728 sta=LOCERR_OK;
2729 } // if not map delete
2730 }
2731 // return status
2732 return sta;
2733} // TCustomImplDS::implProcessMap
2734
2735enum CustomItemOp
2736{
2737 CUSTOM_ITEM_ADD,
2738 CUSTOM_ITEM_ADD_AUGMENTED,
2739 CUSTOM_ITEM_UPDATE,
2740 CUSTOM_ITEM_UPDATE_AUGMENTED,
2741 CUSTOM_ITEM_DELETE
2742};
2743
2744struct TCustomItemAux : public TSyncItemAux
2745{
2746 string fLocalID;
2747 string fRemoteID; // A copy of the original C string, to be on the safe side.
2748 bool fRemoteIDSet;
2749 TSyncOperation fSop;
2750 bool fRemoteHasLatestData;
2751 CustomItemOp fOp;
2752 bool fChangedDBVersion;
2753 bool fChangedNewVersion;
2754};
2755
2756/// process item (according to operation: add/delete/replace - and for future: copy/move)
2757/// @note data items will be sent only after StartWrite()
2758bool TCustomImplDS::implProcessItem(
2759 TSyncItem *aItemP, // the item
2760 TStatusCommand &aStatusCommand
2761) {
2762 #ifdef BASED_ON_BINFILE_CLIENT1
2763 // let binfile handle it if it is active
2764 if (binfileDSActive()) {
1
Taking false branch
2765 return inherited::implProcessItem(aItemP, aStatusCommand);
2766 }
2767 #endif // BASED_ON_BINFILE_CLIENT
2768
2769 // Same approach as in TLocalEngineDS::engProcessRemoteItem:
2770 // backup local state and restore when called again.
2771 bool ok=true;
2772 localstatus sta=LOCERR_OK;
2773 string localID;
2774 const char *remoteID = NULL__null;
2775 // %%% bool RemoteIDKnown=false;
2776 TMapContainer::iterator mappos;
2777 TSyncOperation sop=sop_none;
2778 TMultiFieldItem *augmentedItemP = NULL__null;
2
'augmentedItemP' initialized to a null pointer value
2779 bool remoteHasLatestData;
2780 CustomItemOp op;
2781 bool changedDBVersion, changedNewVersion;
2782
2783 TP_DEFIDX(li);
2784 TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
2785 SYSYNC_TRYtry {
2786 // get casted item pointer
2787 TMultiFieldItem *myitemP = (TMultiFieldItem *)aItemP;
2788
2789 TCustomItemAux *aux = static_cast<TCustomItemAux *>(myitemP->getAux(TSyncItem::CUSTOM_DS));
2790 DEBUGPRINTFX(DBG_DATA,({ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "TCustomImplDS::implProcessItem %p %s, SyncOp=%s, RemoteID='%s', LocalID='%s'"
, myitemP, aux ? "resuming" : "starting", SyncOpNames[myitemP
->getSyncOp()], myitemP->getRemoteID(), myitemP->getLocalID
() ); }
2791 "TCustomImplDS::implProcessItem %p %s, SyncOp=%s, RemoteID='%s', LocalID='%s'",{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "TCustomImplDS::implProcessItem %p %s, SyncOp=%s, RemoteID='%s', LocalID='%s'"
, myitemP, aux ? "resuming" : "starting", SyncOpNames[myitemP
->getSyncOp()], myitemP->getRemoteID(), myitemP->getLocalID
() ); }
2792 myitemP,{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "TCustomImplDS::implProcessItem %p %s, SyncOp=%s, RemoteID='%s', LocalID='%s'"
, myitemP, aux ? "resuming" : "starting", SyncOpNames[myitemP
->getSyncOp()], myitemP->getRemoteID(), myitemP->getLocalID
() ); }
2793 aux ? "resuming" : "starting",{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "TCustomImplDS::implProcessItem %p %s, SyncOp=%s, RemoteID='%s', LocalID='%s'"
, myitemP, aux ? "resuming" : "starting", SyncOpNames[myitemP
->getSyncOp()], myitemP->getRemoteID(), myitemP->getLocalID
() ); }
2794 SyncOpNames[myitemP->getSyncOp()],{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "TCustomImplDS::implProcessItem %p %s, SyncOp=%s, RemoteID='%s', LocalID='%s'"
, myitemP, aux ? "resuming" : "starting", SyncOpNames[myitemP
->getSyncOp()], myitemP->getRemoteID(), myitemP->getLocalID
() ); }
2795 myitemP->getRemoteID(),{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "TCustomImplDS::implProcessItem %p %s, SyncOp=%s, RemoteID='%s', LocalID='%s'"
, myitemP, aux ? "resuming" : "starting", SyncOpNames[myitemP
->getSyncOp()], myitemP->getRemoteID(), myitemP->getLocalID
() ); }
2796 myitemP->getLocalID(){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "TCustomImplDS::implProcessItem %p %s, SyncOp=%s, RemoteID='%s', LocalID='%s'"
, myitemP, aux ? "resuming" : "starting", SyncOpNames[myitemP
->getSyncOp()], myitemP->getRemoteID(), myitemP->getLocalID
() ); }
2797 )){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "TCustomImplDS::implProcessItem %p %s, SyncOp=%s, RemoteID='%s', LocalID='%s'"
, myitemP, aux ? "resuming" : "starting", SyncOpNames[myitemP
->getSyncOp()], myitemP->getRemoteID(), myitemP->getLocalID
() ); }
;
2798 if (aux) {
3
Assuming 'aux' is non-null
4
Taking true branch
2799 // Resuming the function call: restore variables, jump to store
2800 // method call.
2801 localID = aux->fLocalID;
2802 remoteID = aux->fRemoteIDSet ? aux->fRemoteID.c_str() : NULL__null;
5
Assuming the condition is false
6
'?' condition is false
2803 sop = aux->fSop;
2804 remoteHasLatestData = aux->fRemoteHasLatestData;
2805 op = aux->fOp;
2806 changedDBVersion = aux->fChangedDBVersion;
2807 changedNewVersion = aux->fChangedNewVersion;
2808
2809 // Stripped down logic from normal code path below.
2810 // We can't save/restore mapppos because it points into
2811 // a data structure which may change between calls, thus
2812 // invalidating the old iterator.
2813 if (IS_CLIENT(!getSyncAppBase()->isServer())) {
7
Taking true branch
2814 if (!localID.empty() && sop!=sop_add && sop!=sop_wants_add)
8
Assuming the condition is false
2815 mappos=findMapByLocalID(localID.c_str(),mapentry_normal);
2816 else
2817 mappos=fMapTable.end();
2818 }
2819 else {
2820 mappos=findMapByRemoteID(remoteID);
2821 if (mappos!=fMapTable.end()) {
2822 localID = (*mappos).localid;
2823 }
2824 }
2825
2826 aStatusCommand.setStatusCode(510);
2827 switch (op) {
9
Control jumps to 'case CUSTOM_ITEM_ADD_AUGMENTED:' at line 2829
2828 case CUSTOM_ITEM_ADD: goto do_add;
2829 case CUSTOM_ITEM_ADD_AUGMENTED: goto do_add_augmented;
10
Control jumps to line 2931
2830 case CUSTOM_ITEM_UPDATE: goto do_update;
2831 case CUSTOM_ITEM_UPDATE_AUGMENTED: goto do_update_augmented;
2832 case CUSTOM_ITEM_DELETE: goto do_delete;
2833 };
2834 }
2835 if (false) {
2836 // Prepare for resuming the function call. Will only be reached
2837 // via goto with "op" set to something identifying the source of
2838 // the jump.
2839 again:
2840#define CHECK_FOR_AGAIN(_status, _op)if (_status == LOCERR_AGAIN) { op = _op; goto again; } \
2841 if (_status == LOCERR_AGAIN) { \
2842 op = _op; \
2843 goto again; \
2844 }
2845
2846 if (!aux) {
2847 aux = new TCustomItemAux;
2848 myitemP->setAux(TSyncItem::CUSTOM_DS, aux);
2849 }
2850
2851 aux->fLocalID = localID;
2852 aux->fRemoteID = remoteID ? remoteID : "";
2853 aux->fRemoteIDSet = remoteID != NULL__null;
2854 aux->fSop = sop;
2855 // cppcheck-suppress uninitvar
2856 aux->fRemoteHasLatestData = remoteHasLatestData;
2857 // cppcheck-suppress uninitvar
2858 aux->fOp = op;
2859 // cppcheck-suppress uninitvar
2860 aux->fChangedDBVersion = changedDBVersion;
2861 // cppcheck-suppress uninitvar
2862 aux->fChangedNewVersion = changedNewVersion;
2863
2864 aStatusCommand.setStatusCode(LOCERR_AGAIN);
2865 goto error;
2866 }
2867
2868 // - get op
2869 sop = myitemP->getSyncOp();
2870 // - check IDs
2871 if (IS_CLIENT(!getSyncAppBase()->isServer())) {
2872 // Client case: we always get the local ID, except for add
2873 localID=myitemP->getLocalID();
2874 remoteID=myitemP->getRemoteID();
2875 if (!localID.empty() && sop!=sop_add && sop!=sop_wants_add)
2876 mappos=findMapByLocalID(localID.c_str(),mapentry_normal); // for all but sop == sop_add
2877 else
2878 mappos=fMapTable.end(); // if there is no localid or it is an add, we have no map entry yet
2879 }
2880 else {
2881 // Server case: we only know the remote ID
2882 // - get remoteID
2883 remoteID=myitemP->getRemoteID();
2884 // first see if we have a map entry for this remote ID
2885 localID.erase(); // none yet
2886 // Note:
2887 // - even items detected for deletion still have a map item until deletion is confirmed by the remote party,
2888 // so we'll be able to update already "deleted" items (in case they are not really gone, but only invisible in the sync set)
2889 // - we can use mapflag_pendingDeleteStatus (which does not need persistence in the DB, so works even for not resume-enabled backends)
2890 // to keep still existing and deleted items apart.
2891 mappos=findMapByRemoteID(remoteID); // search for it
2892 if (mappos!=fMapTable.end()) {
2893 localID = (*mappos).localid; // assign it if we have it
2894 }
2895 }
2896 // - now perform op
2897 aStatusCommand.setStatusCode(510); // default DB error
2898 remoteHasLatestData = false;
2899 switch (sop) {
2900 /// @todo sop_copy is now implemented by read/add sequence
2901 /// in localEngineDS, but will be moved here later possibly
2902 case sop_add :
2903 // check for duplicated add
2904 // Notes:
2905 // - server must check it here, because map lookup is needed. Contrarily, client
2906 // can check it on localengineds level against the pending maps list with isAddFromLastSession().
2907 // - if mapflag_pendingDeleteStatus is set, the item still has a map entry, but does not exist in the DB any more
2908 // so do not report 418 here!
2909 if (IS_SERVER(getSyncAppBase()->isServer()) && mappos!=fMapTable.end() && ((*mappos).mapflags & mapflag_pendingDeleteStatus0x00000008)==0) {
2910 // we already know this item (and it was not already detected as deleted from the DB, so should exist there)
2911 // - status "already exists"
2912 aStatusCommand.setStatusCode(418);
2913 ok = false;
2914 break;
2915 }
2916 // add item and retrieve new localID for it
2917 do_add:
2918 sta = apiAddItem(*myitemP,localID);
2919 CHECK_FOR_AGAIN(sta, CUSTOM_ITEM_ADD)if (sta == LOCERR_AGAIN) { op = CUSTOM_ITEM_ADD; goto again; };
2920 myitemP->setLocalID(localID.c_str()); // possibly following operations need to be based on new localID returned by add
2921 // check for backend asking engine to do a merge
2922 if (sta==DB_Conflict) {
2923 // DB has detected item conflicts with data already stored in the database and
2924 // request merging current data from the backend with new data before storing.
2925 augmentedItemP = mergeWithDatabaseVersion(myitemP, changedDBVersion, changedNewVersion);
2926 if (augmentedItemP==NULL__null)
2927 sta = DB_Error; // no item found, DB error
2928 else {
2929 // store augmented version back to DB only if modified
2930 do_add_augmented:
2931 if (changedDBVersion)
11
Assuming 'changedDBVersion' is not equal to 0
12
Taking true branch
2932 sta = apiUpdateItem(*augmentedItemP);
13
Forming reference to null pointer
2933 else
2934 sta = LOCERR_OK;
2935 CHECK_FOR_AGAIN(sta, CUSTOM_ITEM_ADD_AUGMENTED)if (sta == LOCERR_AGAIN) { op = CUSTOM_ITEM_ADD_AUGMENTED; goto
again; }
;
2936 // in server case, further process like backend merge (but no need to fetch again, we just keep augmentedItemP)
2937 if (IS_SERVER(getSyncAppBase()->isServer()) && sta==LOCERR_OK) {
2938 // TLocalEngineDS::engProcessRemoteItemAsServer() in
2939 // localengineds.cpp already counted the item as added
2940 // because it didn't know that special handling would be
2941 // needed. Instead of a complicated mechanism to report
2942 // back the actual outcome, let's fix the statistics
2943 // here.
2944 fLocalItemsAdded--;
2945 if (changedDBVersion)
2946 fLocalItemsUpdated++;
2947 sta = DB_DataMerged;
2948 }
2949 // in the processing below avoid sending an unnecessare Replace
2950 // if the data sent by the peer already was up-to-date
2951 if (!changedNewVersion)
2952 remoteHasLatestData = true;
2953 }
2954 }
2955 if (IS_SERVER(getSyncAppBase()->isServer())) {
2956 #ifdef SYSYNC_SERVER1
2957 if (sta==DB_DataMerged || sta==DB_DataReplaced) {
2958 // while adding, data was merged with pre-existing data from...
2959 // ..either data external from the sync set, such as augmenting a contact with info from a third-party lookup
2960 // ..or another item pre-existing in the sync set.
2961 PDEBUGPRINTFX(DBG_DATA,("Database adapter indicates that added item was merged with pre-existing data (status 207/209/409)")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ("Database adapter indicates that added item was merged with pre-existing data (status 207/209/409)"
); }
;
2962 // check if the item resulting from merge is known by the client already (in it's pre-merge form, that is)
2963 TMapContainer::iterator conflictingMapPos = findMapByLocalID(localID.c_str(), mapentry_normal);
2964 bool remoteAlreadyKnowsItem = conflictingMapPos!=fMapTable.end();
2965 // also check if we have a (pre-merge) operation pending for that item already
2966 TSyncItem *conflictingItemP = getConflictingItemByLocalID(myitemP);
2967 if (conflictingItemP) {
2968 // cancel any pending operation for the original item.
2969 dontSendItemAsServer(conflictingItemP);
2970 }
2971 // If client already knows that item, we must propagate the merge to the client
2972 // by deleting the original item (in addition to sending the update of the merge)
2973 if (remoteAlreadyKnowsItem) {
2974 PDEBUGPRINTFX(DBG_DATA,({ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "Merge occured with an item already known remotely (localID=%s, remoteID=%s) -> delete duplicate from client"
, (*conflictingMapPos).localid.c_str(), (*conflictingMapPos).
remoteid.c_str() ); }
2975 "Merge occured with an item already known remotely (localID=%s, remoteID=%s) -> delete duplicate from client",{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "Merge occured with an item already known remotely (localID=%s, remoteID=%s) -> delete duplicate from client"
, (*conflictingMapPos).localid.c_str(), (*conflictingMapPos).
remoteid.c_str() ); }
2976 (*conflictingMapPos).localid.c_str(),{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "Merge occured with an item already known remotely (localID=%s, remoteID=%s) -> delete duplicate from client"
, (*conflictingMapPos).localid.c_str(), (*conflictingMapPos).
remoteid.c_str() ); }
2977 (*conflictingMapPos).remoteid.c_str(){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "Merge occured with an item already known remotely (localID=%s, remoteID=%s) -> delete duplicate from client"
, (*conflictingMapPos).localid.c_str(), (*conflictingMapPos).
remoteid.c_str() ); }
2978 )){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ( "Merge occured with an item already known remotely (localID=%s, remoteID=%s) -> delete duplicate from client"
, (*conflictingMapPos).localid.c_str(), (*conflictingMapPos).
remoteid.c_str() ); }
;
2979 // client already knows an item with that server-side localID
2980 // - check if it is the same item as the added one from a server's perspective
2981 // (this should not normally not be the case, as otherwise we should not have
2982 // tried to add it in the first place - check above should have generated 418 error)
2983 bool sameRemoteItem = (*conflictingMapPos).remoteid==myitemP->getRemoteID();
2984 if (sameRemoteItem) {
2985 PDEBUGPRINTFX(DBG_ERROR,("Consistency error: despite being added new, this remoteID is already known!?")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("Consistency error: despite being added new, this remoteID is already known!?"
); }
;
2986 }
2987 else {
2988 // create delete for now duplicate item on client
2989 TSyncItem *duplDelP = newItemForRemote(myitemP->getTypeID());
2990 if (duplDelP) {
2991 // - setup delete item
2992 duplDelP->setRemoteID((*conflictingMapPos).remoteid.c_str());
2993 duplDelP->clearLocalID();
2994 duplDelP->setSyncOp(sop_delete);
2995 // - add it to the list of changes to be sent to the client later
2996 SendItemAsServer(duplDelP);
2997 }
2998 }
2999 }
3000 // if backend has not replaced, but merely merged data, we're done. Otherwise, client needs to be updated with
3001 // merged/augmented version of the data
3002 if (!remoteHasLatestData && sta!=DB_DataReplaced) {
3003 // now create a replace command to update the item added from the client with the merge result
3004 // - this is like forcing a conflict, i.e. this loads the item by local/remoteid and adds it to
3005 // the to-be-sent list of the server.
3006 if (augmentedItemP) {
3007 // augmented version was created in engine, just add that version to the list of items to be sent
3008 SendItemAsServer(augmentedItemP); // takes ownership of augmentedItemP
3009 augmentedItemP = NULL__null;
3010 }
3011 else {
3012 // augmented version was created in backend, fetch it now and add to list of items to be sent
3013 SendDBVersionOfItemAsServer(myitemP);
3014 }
3015 }
3016 sta = LOCERR_OK; // otherwise, treat as ok
3017 }
3018 #endif // SYSYNC_SERVER
3019 } // server
3020 // - we don't need the augmented item any more if it still exists at this point
3021 if (augmentedItemP) {
3022 delete augmentedItemP; augmentedItemP = NULL__null;
3023 }
3024 if (sta!=LOCERR_OK) {
3025 aStatusCommand.setStatusCode(sta);
3026 ok=false;
3027 }
3028 else {
3029 // added ok
3030 // - save what is needed for finalisation
3031 if (fNeedFinalisation) {
3032 myitemP->setLocalID(localID.c_str()); // finalisation needs to know the local ID
3033 queueForFinalisation(myitemP);
3034 }
3035 // - status ok
3036 aStatusCommand.setStatusCode(201); // item added
3037 // - add or update map entry (in client case, remoteID is irrelevant and possibly is not saved)
3038 modifyMap(mapentry_normal,localID.c_str(),remoteID,0,false);
3039 ok=true;
3040 }
3041 break;
3042 case sop_replace :
3043 if (mappos==fMapTable.end()) {
3044 // not found in map table
3045 aStatusCommand.setStatusCode(404);
3046 ok=false;
3047 }
3048 else {
3049 // - make sure item has local ID set
3050 myitemP->setLocalID(localID.c_str());
3051 // update item
3052 do_update:
3053 sta = apiUpdateItem(*myitemP);
3054 CHECK_FOR_AGAIN(sta, CUSTOM_ITEM_UPDATE)if (sta == LOCERR_AGAIN) { op = CUSTOM_ITEM_UPDATE; goto again
; }
;
3055 if (sta==DB_Conflict) {
3056 // DB has detected item conflicts with data already stored in the database and
3057 // request merging current data from the backend with new data before storing.
3058 augmentedItemP = mergeWithDatabaseVersion(myitemP, changedDBVersion, changedNewVersion);
3059 if (augmentedItemP==NULL__null)
3060 sta = DB_Error; // no item found, DB error
3061 else {
3062 // store augmented version back to DB only if modified
3063 do_update_augmented:
3064 if (changedDBVersion)
3065 sta = apiUpdateItem(*augmentedItemP);
3066 else
3067 sta = LOCERR_OK;
3068 CHECK_FOR_AGAIN(sta, CUSTOM_ITEM_UPDATE_AUGMENTED)if (sta == LOCERR_AGAIN) { op = CUSTOM_ITEM_UPDATE_AUGMENTED;
goto again; }
;
3069 delete augmentedItemP; // forget now
3070 }
3071 }
3072 // now check final status
3073 if (sta!=LOCERR_OK) {
3074 aStatusCommand.setStatusCode(sta);
3075 ok=false;
3076 }
3077 else {
3078 // updated ok
3079 // - save what is needed for finalisation
3080 if (fNeedFinalisation)
3081 queueForFinalisation(myitemP);
3082 // - status ok
3083 aStatusCommand.setStatusCode(200); // item replaced ok
3084 ok=true;
3085 }
3086 }
3087 break;
3088 case sop_delete :
3089 case sop_archive_delete :
3090 case sop_soft_delete :
3091 if (mappos==fMapTable.end()) {
3092 // not found in map table means that remote is trying to
3093 // delete an item that wasn't mapped before. This is different from
3094 // the case below when the actual item is not there any more, but
3095 // the map still existed (-> 211)
3096 aStatusCommand.setStatusCode(404);
3097 ok=false;
3098 }
3099 else {
3100 // - make sure item has local ID set
3101 myitemP->setLocalID(localID.c_str());
3102 // delete item
3103 do_delete:
3104 sta = apiDeleteItem(*myitemP);
3105 CHECK_FOR_AGAIN(sta, CUSTOM_ITEM_DELETE)if (sta == LOCERR_AGAIN) { op = CUSTOM_ITEM_DELETE; goto again
; }
;
3106 if (sta!=LOCERR_OK) {
3107 // not found is reported as successful 211 status, because result is ok (item deleted, whatever reason)
3108 if (sta==404)
3109 sta=211; // ok, but item was not there any more
3110 else
3111 ok=false; // others are real errors
3112 aStatusCommand.setStatusCode(sta);
3113 }
3114 else {
3115 // - ok
3116 aStatusCommand.setStatusCode(200); // item deleted ok
3117 ok=true;
3118 }
3119 if (ok) {
3120 // delete map entry anyway
3121 // - mark the map entry for deletion
3122 modifyMap(mapentry_normal,localID.c_str(),NULL__null,0,true);
3123 }
3124 }
3125 break;
3126 default :
3127 SYSYNC_THROW(TSyncException("Unknown sync op in TCustomImplDS::implProcessItem"))throw TSyncException("Unknown sync op in TCustomImplDS::implProcessItem"
)
;
3128 } // switch
3129 if (ok) {
3130 // successful, save new localID in item
3131 myitemP->setLocalID(localID.c_str());
3132 TP_START(fSessionP->fTPInfo,li);
3133 return true;
3134 }
3135 else {
3136 TP_START(fSessionP->fTPInfo,li);
3137 return false;
3138 }
3139 }
3140 SYSYNC_CATCH (exception &e)catch(exception &e) {
3141 PDEBUGPRINTFX(DBG_ERROR,("******** TCustomImplDS::implProcessItem exception: %s",e.what())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("******** TCustomImplDS::implProcessItem exception: %s"
,e.what()); }
;
3142 aStatusCommand.setStatusCode(510);
3143 goto error;
3144 SYSYNC_ENDCATCH}
3145 SYSYNC_CATCH (...)catch(...) {
3146 PDEBUGPRINTFX(DBG_ERROR,("******** TCustomImplDS::implProcessItem unknown exception")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("******** TCustomImplDS::implProcessItem unknown exception"
); }
;
3147 goto error;
3148 SYSYNC_ENDCATCH}
3149error:
3150 // Switch back to previous timer
3151 TP_START(fSessionP->fTPInfo,li);
3152 return false;
3153} // TCustomImplDS::implProcessItem
3154
3155
3156
3157// private helper to prepare for apiSaveAdminData()
3158localstatus TCustomImplDS::SaveAdminData(bool aSessionFinished, bool aSuccessful)
3159{
3160 TMapContainer::iterator pos;
3161 localstatus sta=LOCERR_OK;
3162 // calculate difference between current and previous state of tempGUID maps or pending maps
3163 // - mark all non-main map entries as deleted (those that still exist will be re-added later)
3164 // - also do some clean-up in case of successful end-of-session
3165 pos=fMapTable.begin();
3166 while (pos!=fMapTable.end()) {
3167 if ((*pos).entrytype!=mapentry_normal) {
3168 // Note: this is not strictly needed any more, as non-normal maps are
3169 // now already entered into fMapTable with deleted flag set
3170 // (to make sure they don't get used by accident)
3171 (*pos).deleted=true;
3172 }
3173 else if (!(*pos).deleted) {
3174 // in case of map table without flags, we must get rid of all non-real maps
3175 if (!dsResumeSupportedInDB() && aSessionFinished) {
3176 // For client, remoteid is irrelevant and can well be empty
3177 // Map entries exist for those items that are not newly added on the client
3178 // For server, maps w/o remoteid are not really mapped and must not be saved when
3179 // we have no flags to mark this special conditon (mapflag_pendingAddConfirm)
3180 if (IS_SERVER(getSyncAppBase()->isServer()) && (*pos).remoteid.empty()) {
3181 // no remoteid -> this is not a real map, we cannot represent it w/o resume support (=flags) in map table
3182 DEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("LocalID='%s' has no remoteID - cannot be stored in non-DS-1.2 Map DB -> removed map",(*pos).localid.c_str())){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("LocalID='%s' has no remoteID - cannot be stored in non-DS-1.2 Map DB -> removed map"
,(*pos).localid.c_str()); }
;
3183 if ((*pos).added) {
3184 // was never added to DB, so no need to delete it in DB either - just forget it
3185 TMapContainer::iterator delpos=pos++;
3186 fMapTable.erase(delpos);
3187 continue;
3188 }
3189 else {
3190 // is already in DB - mark deleted
3191 (*pos).deleted=true;
3192 }
3193 }
3194 else {
3195 // clear all specials
3196 (*pos).mapflags=0;
3197 (*pos).savedmark=false;
3198 (*pos).markforresume=false;
3199 }
3200 }
3201 // normal, undeleted map entry, check for cleanup
3202 else if (
3203 aSessionFinished && aSuccessful &&
3204 ((*pos).mapflags & mapflag_pendingAddConfirm0x00000010)
3205 ) {
3206 // successful end of session - we can forget pending add confirmations (as the add commands apparently never reached the remote at all)
3207 // Note: for clients, maps can well have an empty remoteid (because it does not need to be saved)
3208 if (IS_SERVER(getSyncAppBase()->isServer()) && (*pos).remoteid.empty()) {
3209 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Successful end of session but localID='%s' has no remoteID and pendingAddConfirm still set -> removed map",(*pos).localid.c_str())){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("Successful end of session but localID='%s' has no remoteID and pendingAddConfirm still set -> removed map"
,(*pos).localid.c_str()); }
;
3210 // if not mapped, this will be a re-add in the next session, so forget it for now
3211 if ((*pos).added) {
3212 // was never added to DB, so no need to delete it in DB either - just forget it
3213 TMapContainer::iterator delpos=pos++;
3214 fMapTable.erase(delpos);
3215 continue;
3216 }
3217 else {
3218 // is already in DB - mark deleted
3219 (*pos).deleted=true;
3220 }
3221 }
3222 else {
3223 // For server: is mapped, which means that it now exists in the client - just clean mapflag_pendingAddConfirm
3224 // For client: just clean the pendingAddConfirm
3225 // Note: maps like this should not exist at this time - as at end of a successful session all items should
3226 // have got confirmation.
3227 PDEBUGPRINTFX(DBG_ERROR,("Apparently successful end of session - but localID='%s' has pendingAddConfirm still set: sync may not be fully complete",(*pos).localid.c_str())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("Apparently successful end of session - but localID='%s' has pendingAddConfirm still set: sync may not be fully complete"
,(*pos).localid.c_str()); }
;
3228 (*pos).mapflags &= ~mapflag_pendingAddConfirm0x00000010; // keep mapflag_pendingStatus for documentary/debug purposes
3229 (*pos).changed = true; // make sure it gets written to DB
3230 }
3231 }
3232 }
3233 // increment here only so we can do "continue" w/o pos increment after delete
3234 pos++;
3235 } // for all map entries
3236 if (dsResumeSupportedInDB()) {
3237 // If we have different map entry types - re-add the special entries from the separate lists
3238 // Note: these entries are already in the global map table, but with the deleted flag set.
3239 // Here those that still exist now will be re-activated (without saving them again if not needed)
3240 TStringToStringMap::iterator spos;
3241 if (IS_CLIENT(!getSyncAppBase()->isServer())) {
3242 #ifdef SYSYNC_CLIENT1
3243 // - now pending maps (unsent ones)
3244 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("SaveAdminData: adding %ld entries from fPendingAddMaps as mapentry_pendingmap",(long)fPendingAddMaps.size())){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("SaveAdminData: adding %ld entries from fPendingAddMaps as mapentry_pendingmap"
,(long)fPendingAddMaps.size()); }
;
3245 for (spos=fPendingAddMaps.begin();spos!=fPendingAddMaps.end();spos++) {
3246 string locID = (*spos).first;
3247 dsFinalizeLocalID(locID); // make sure we have the permanent version in case datastore implementation did deliver temp IDs
3248 modifyMap(mapentry_pendingmap, locID.c_str(), (*spos).second.c_str(), 0, false);
3249 }
3250 // - now pending maps (sent, but not seen status yet)
3251 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("SaveAdminData: adding %ld entries from fUnconfirmedMaps as mapentry_pendingmap/mapflag_pendingMapStatus",(long)fUnconfirmedMaps.size())){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("SaveAdminData: adding %ld entries from fUnconfirmedMaps as mapentry_pendingmap/mapflag_pendingMapStatus"
,(long)fUnconfirmedMaps.size()); }
;
3252 for (spos=fUnconfirmedMaps.begin();spos!=fUnconfirmedMaps.end();spos++) {
3253 modifyMap(mapentry_pendingmap, (*spos).first.c_str(), (*spos).second.c_str(), mapflag_pendingMapStatus0x00000002, false);
3254 }
3255 #endif
3256 }
3257 else {
3258 #ifdef SYSYNC_SERVER1
3259 // - the tempguid maps
3260 PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("SaveAdminData: adding %ld entries from fTempGUIDMap as mapentry_tempidmap",(long)fTempGUIDMap.size())){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040
+0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000
).DebugPrintfLastMask ("SaveAdminData: adding %ld entries from fTempGUIDMap as mapentry_tempidmap"
,(long)fTempGUIDMap.size()); }
;
3261 for (spos=fTempGUIDMap.begin();spos!=fTempGUIDMap.end();spos++) {
3262 modifyMap(mapentry_tempidmap, (*spos).second.c_str(), (*spos).first.c_str(), 0, false);
3263 }
3264 #endif
3265 }
3266 }
3267 sta=apiSaveAdminData(aSessionFinished,aSuccessful);
3268 if (sta!=LOCERR_OK) {
3269 PDEBUGPRINTFX(DBG_ERROR,("SaveAdminData failed, err=%hd",sta)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("SaveAdminData failed, err=%hd"
,sta); }
;
3270 }
3271 return sta;
3272} // TCustomImplDS::SaveAdminData
3273
3274#endif // not BINFILE_ALWAYS_ACTIVE
3275
3276
3277// save end of session state
3278localstatus TCustomImplDS::implSaveEndOfSession(bool aUpdateAnchors)
3279{
3280 localstatus sta=LOCERR_OK;
3281 PDEBUGBLOCKCOLL("SaveEndOfSession")getDbgLogger()->DebugOpenBlock( "SaveEndOfSession",__null,
true)
;
3282 // update TCustomImplDS dsSavedAdmin variables (other levels have already updated their variables
3283 if (aUpdateAnchors) {
3284 if (!fRefreshOnly || (fRefreshOnly && fCacheData) || fSlowSync) {
3285 // This was really a two-way sync or we implicitly know that
3286 // we are now in sync with remote (like after one-way-from-remote refresh = reload local)
3287 #ifdef BASED_ON_BINFILE_CLIENT1
3288 if (!binfileDSActive())
3289 #endif // BASED_ON_BINFILE_CLIENT
3290 {
3291 #ifndef BINFILE_ALWAYS_ACTIVE
3292 // Note: in case of BASED_ON_BINFILE_CLIENT, these updates will be done by binfileds
3293 // (also note that fPreviousToRemoteSyncCmpRef has different semantics in BASED_ON_BINFILE_CLIENT,
3294 // as it serves as a last-changelog-update reference then)
3295 // But here, fPreviousToRemoteSyncCmpRef is what it seems - the timestamp corresponding to last sync to remote
3296 if (fConfigP->fSyncTimeStampAtEnd) {
3297 // if datastore cannot explicitly set modification timestamps, best time to save is current time
3298 fPreviousToRemoteSyncCmpRef = fAgentP->getDatabaseNowAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)));
3299 }
3300 else {
3301 // if datastore can set modification timestamps, best time to save is start of sync
3302 fPreviousToRemoteSyncCmpRef = fCurrentSyncTime;
3303 }
3304 #endif
3305 }
3306 // also update opaque reference string possibly needed in DS API implementations
3307 fPreviousToRemoteSyncIdentifier = fCurrentSyncIdentifier;
3308 PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("updating sync token (fPreviousToRemoteSyncIdentifier) from %s to current sync token %s",fPreviousToRemoteSyncIdentifier.c_str(),fCurrentSyncIdentifier.c_str())){ if (((0x00000040 +0x02000000 +0x80000000) & getDbgMask(
)) == (0x00000040 +0x02000000 +0x80000000)) getDbgLogger()->
setNextMask(0x00000040 +0x02000000 +0x80000000).DebugPrintfLastMask
("updating sync token (fPreviousToRemoteSyncIdentifier) from %s to current sync token %s"
,fPreviousToRemoteSyncIdentifier.c_str(),fCurrentSyncIdentifier
.c_str()); }
;
3309 } else {
3310 PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("keeping old sync token (fPreviousToRemoteSyncIdentifier) %s instead of updating to current sync token %s",fPreviousToRemoteSyncIdentifier.c_str(),fCurrentSyncIdentifier.c_str())){ if (((0x00000040 +0x02000000 +0x80000000) & getDbgMask(
)) == (0x00000040 +0x02000000 +0x80000000)) getDbgLogger()->
setNextMask(0x00000040 +0x02000000 +0x80000000).DebugPrintfLastMask
("keeping old sync token (fPreviousToRemoteSyncIdentifier) %s instead of updating to current sync token %s"
,fPreviousToRemoteSyncIdentifier.c_str(),fCurrentSyncIdentifier
.c_str()); }
;
3311 }
3312 // updating anchor means invalidating last Suspend
3313 fPreviousSuspendCmpRef = fPreviousToRemoteSyncCmpRef; // setting to current reference can do less harm than setting it to zero
3314 fPreviousSuspendIdentifier.erase();
3315 }
3316 #ifdef BASED_ON_BINFILE_CLIENT1
3317 if (binfileDSActive()) {
3318 // if we sit on top of activated binfile, let binfile do the actual end-if-session work
3319 // (updates of cmprefs etc. are done at binfile level again).
3320 sta = inherited::implSaveEndOfSession(aUpdateAnchors);
3321 }
3322 else
3323 #endif // BASED_ON_BINFILE_CLIENT
3324 {
3325 // save admin data myself now
3326 sta=SaveAdminData(true,aUpdateAnchors); // end of session
3327 // we can foget the maps now
3328 fMapTable.clear();
3329 }
3330 PDEBUGENDBLOCK("SaveEndOfSession")getDbgLogger()->DebugCloseBlock( "SaveEndOfSession");
3331 return sta;
3332} // TCustomImplDS::implSaveEndOfSession
3333
3334
3335// - end write with commit
3336bool TCustomImplDS::implEndDataWrite(void)
3337{
3338 localstatus sta=LOCERR_OK;
3339 TP_DEFIDX(li);
3340 TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
3341 SYSYNC_TRYtry {
3342 // first make sure data writing ends (and obtain current sync identifier)
3343 sta = apiEndDataWrite(fCurrentSyncIdentifier);
3344 }
3345 SYSYNC_CATCH (exception &e)catch(exception &e) {
3346 PDEBUGPRINTFX(DBG_ERROR,("******** implEndDataWrite exception: %s",e.what())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("******** implEndDataWrite exception: %s"
,e.what()); }
;
3347 TP_START(fSessionP->fTPInfo,li);
3348 return false;
3349 SYSYNC_ENDCATCH}
3350 TP_START(fSessionP->fTPInfo,li);
3351 #ifdef BASED_ON_BINFILE_CLIENT1
3352 if (binfileDSActive()) {
3353 // binfile level must be called as well
3354 sta = inherited::implEndDataWrite();
3355 }
3356 #endif
3357 return sta;
3358} // TCustomImplDS::implEndDataWrite
3359
3360
3361// delete sync set one by one
3362localstatus TCustomImplDS::zapSyncSetOneByOne(void)
3363{
3364 TSyncSetList::iterator pos;
3365 localstatus sta;
3366 TStatusCommand dummy(getSession());
3367 // check if we need to apply filters
3368 bool filteredDelete = fFilteringNeededForAll || fFilteringNeeded;
3369 TSyncItem *delitemP = NULL__null;
3370 if (!filteredDelete) {
3371 PDEBUGPRINTFX(DBG_DATA,("Zapping datastore unfiltered: deleting %ld items from database",(long)fSyncSetList.size())){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ("Zapping datastore unfiltered: deleting %ld items from database"
,(long)fSyncSetList.size()); }
;
3372 }
3373 else {
3374 PDEBUGPRINTFX(DBG_DATA,("Zapping datastore with filter: deleting only filter passing items of max %ld items",(long)fSyncSetList.size())){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ("Zapping datastore with filter: deleting only filter passing items of max %ld items"
,(long)fSyncSetList.size()); }
;
3375 }
3376 long tot = fSyncSetList.size();
3377 long n = 0;
3378 for (pos=fSyncSetList.begin(); pos!=fSyncSetList.end(); ++pos) {
3379 if (filteredDelete) {
3380 // we need to inspect further, as we may NOT delete the entire sync set
3381 // - get the item with data (we become owner of it!)
3382 getItemFromSyncSetItem(*pos,delitemP);
3383 // - check filters
3384 bool passes=postFetchFiltering(delitemP);
3385 if (!passes) {
3386 tot--; // one less than initially assumed
3387 continue; // don't delete this one, it does not pass the filter
3388 }
3389 // - delete now
3390 PDEBUGPRINTFX(DBG_DATA,("- item '%s' passes filter -> deleting",delitemP->getLocalID())){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ("- item '%s' passes filter -> deleting"
,delitemP->getLocalID()); }
;
3391 }
3392 else {
3393 // all items loaded need to be deleted
3394 // - create dummy item
3395 delitemP = newItemForRemote(ity_multifield);
3396 delitemP->setLocalID((*pos)->localid.c_str());
3397 }
3398 // delete
3399 sta = apiDeleteItem(*(static_cast<TMultiFieldItem *>(delitemP)));
3400 n++;
3401 DB_PROGRESS_EVENT(this,pev_deleting,n,tot,0)this->getSession()->NotifySessionProgressEvent(pev_deleting
,this->getDSConfig(),n,tot,0)
;
3402 // forget the item
3403 delete delitemP;
3404 // success or "211 - not deleted" is ok.
3405 if (sta!=LOCERR_OK && sta!=211) return sta;
3406 }
3407 return LOCERR_OK; // zapped ok
3408} // TCustomImplDS::zapSyncSetOneByOne
3409
3410
3411// private helper: get item with data from sync set list. Retrieves item if not already
3412// there from loading the sync set
3413// Note: cannot be called with aSyncSetItemP==NULL; only aSyncSetItemP->itemP may be NULL
3414localstatus TCustomImplDS::getItemFromSyncSetItem(TSyncSetItem *aSyncSetItemP, TSyncItem *&aItemP)
3415{
3416 if (aSyncSetItemP->itemP) {
3417 // already fetched - pass it to caller and remove link in syncsetitem
3418 aItemP = aSyncSetItemP->itemP;
3419 aSyncSetItemP->itemP = NULL__null; // syncsetitem does not own it any longer
3420 }
3421 else {
3422 // item not yet fetched (or already retrieved once), fetch it now
3423 // - create new empty TMultiFieldItem
3424 aItemP =
3425 (TMultiFieldItem *) newItemForRemote(ity_multifield);
3426 if (!aItemP)
3427 return 510;
3428 // - assign local id, as it is required e.g. by DoDataSubstitutions
3429 aItemP->setLocalID(aSyncSetItemP->localid.c_str());
3430 // - set default operation
3431 aItemP->setSyncOp(sop_replace);
3432 // Now fetch item (read phase)
3433 localstatus sta = apiFetchItem(*((TMultiFieldItem *)aItemP),true,aSyncSetItemP);
3434 if (sta!=LOCERR_OK) {
3435 delete aItemP;
3436 aItemP = NULL__null;
3437 }
3438 return sta;
3439 }
3440 // ok
3441 return LOCERR_OK;
3442} // TCustomImplDS::getItemFromSyncSetItem
3443
3444
3445#ifndef BINFILE_ALWAYS_ACTIVE
3446
3447// - save status information required to possibly perform a resume (as passed to datastore with
3448// implMarkOnlyUngeneratedForResume() and implMarkItemForResume())
3449// (or, in case the session is really complete, make sure that no resume state is left)
3450localstatus TCustomImplDS::implSaveResumeMarks(void)
3451{
3452 #ifdef BASED_ON_BINFILE_CLIENT1
3453 // let binfile handle it if it is active
3454 if (binfileDSActive()) {
3455 return inherited::implSaveResumeMarks();
3456 }
3457 #endif // BASED_ON_BINFILE_CLIENT
3458
3459 // update anchoring info for resume
3460 if (fConfigP->fSyncTimeStampAtEnd) {
3461 // if datastore cannot explicitly set modification timestamps, best time to save is current time
3462 fPreviousSuspendCmpRef = fAgentP->getDatabaseNowAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)));
3463 }
3464 else {
3465 // if datastore can set modification timestamps, best time to save is start of sync
3466 fPreviousSuspendCmpRef = fCurrentSyncTime;
3467 }
3468 // also update opaque reference string possibly needed in DS API implementations
3469 fPreviousSuspendIdentifier = fCurrentSyncIdentifier;
3470 // save admin data now
3471 return SaveAdminData(false,false); // not end of session, not successful end either
3472} // TCustomImplDS::implSaveResumeMarks
3473
3474
3475#endif // not BINFILE_ALWAYS_ACTIVE
3476
3477
3478#ifdef BASED_ON_BINFILE_CLIENT1
3479
3480// Connecting methods when CustomImplDS is used on top of BinFileImplDS
3481
3482// Note: these are defined by BinFileImplDS and are ONLY CALLED IF BinFileImplDS is
3483// active. In setups where we can switch off the intermediate binfile layer,
3484// these routines are never called and can't harm
3485
3486// private helper
3487localstatus TCustomImplDS::makeSyncSetLoaded(bool aNeedAll)
3488{
3489 localstatus sta = LOCERR_OK; // assume loaded ok
3490 if (!fSyncSetLoaded) {
3491 // not yet loaded, try to load
3492 PDEBUGBLOCKFMTCOLL(("ReadSyncSet","Reading Sync Set from Database","datastore=%s",getName()))getDbgLogger()->DebugOpenBlockCollapsed ("ReadSyncSet","Reading Sync Set from Database"
,"datastore=%s",getName())
;
3493 SYSYNC_TRYtry {
3494 sta = apiReadSyncSet(aNeedAll);
3495 PDEBUGENDBLOCK("ReadSyncSet")getDbgLogger()->DebugCloseBlock( "ReadSyncSet");
3496 }
3497 SYSYNC_CATCH(exception &e)catch(exception &e) {
3498 PDEBUGPRINTFX(DBG_ERROR,("makeSyncSetLoaded exception: %s",e.what())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("makeSyncSetLoaded exception: %s"
,e.what()); }
;
3499 sta=510;
3500 // end of DB read
3501 PDEBUGENDBLOCK("ReadSyncSet")getDbgLogger()->DebugCloseBlock( "ReadSyncSet");
3502 SYSYNC_ENDCATCH}
3503 if (sta==LOCERR_OK)
3504 fSyncSetLoaded=true; // is now loaded
3505 }
3506 return sta; // ok only if now loaded
3507} // TCustomImplDS::makeSyncSetLoaded
3508
3509
3510/// get first item from the sync set. Caller obtains ownership if aItemP is not NULL after return
3511/// @return false if no item found
3512bool TCustomImplDS::getFirstItem(TSyncItem *&aItemP)
3513{
3514 // reset the iterator
3515 fSyncSetPos = fSyncSetList.begin();
3516 // now get first item's info
3517 return getNextItem(aItemP);
3518} // TCustomImplDS::getFirstItem
3519
3520
3521/// get next item from the sync set. Caller obtains ownership if aItemP is not NULL after return
3522/// @return false if no item found
3523bool TCustomImplDS::getNextItem(TSyncItem *&aItemP)
3524{
3525 if (!fSyncSetLoaded)
3526 return false; // no syncset, nothing to report
3527 if (fSyncSetPos!=fSyncSetList.end()) {
3528 // get the info
3529 TSyError sta = getItemFromSyncSetItem(*fSyncSetPos,aItemP);
3530 if (sta==LOCERR_OK) {
3531 // advance to next item in sync set
3532 fSyncSetPos++;
3533 // successful
3534 return true;
3535 }
3536 }
3537 // no more items (or problem getting item)
3538 return false;
3539} // TCustomImplDS::getNextItem
3540
3541
3542
3543#ifdef CHANGEDETECTION_AVAILABLE1
3544
3545/// get item's ID and modification status from the sync set, not including data
3546/// @return false if no item found
3547bool TCustomImplDS::getFirstItemInfo(localid_out_t &aLocalID, bool &aItemHasChanged)
3548{
3549 // reset the iterator
3550 fSyncSetPos = fSyncSetList.begin();
3551 // now get first item's info
3552 return getNextItemInfo(aLocalID, aItemHasChanged);
3553} // TCustomImplDS::getFirstItemInfo
3554
3555
3556
3557/// get next item's ID and modification status from the sync set, not including data
3558/// @return false if no item found
3559bool TCustomImplDS::getNextItemInfo(localid_out_t &aLocalID, bool &aItemHasChanged)
3560{
3561 if (!fSyncSetLoaded)
3562 return false; // no syncset, nothing to report
3563 if (fSyncSetPos!=fSyncSetList.end()) {
3564 // get the info
3565 TSyncSetItem *syncsetitemP = (*fSyncSetPos);
3566 // - ID
3567 STR_TO_LOCALID(syncsetitemP->localid.c_str(),aLocalID)(aLocalID=(char *)syncsetitemP->localid.c_str());
3568 // - changeflag
3569 aItemHasChanged = syncsetitemP->isModified;
3570 // advance to next item in sync set
3571 fSyncSetPos++;
3572 // ok
3573 return true;
3574 }
3575 // no more items
3576 return false;
3577} // TCustomImplDS::getNextItemInfo
3578
3579#endif // CHANGEDETECTION_AVAILABLE
3580
3581
3582/// get item by local ID from the sync set. Caller obtains ownership if aItemP is not NULL after return
3583/// @return != LOCERR_OK if item with specified ID is not found.
3584localstatus TCustomImplDS::getItemByID(localid_t aLocalID, TSyncItem *&aItemP)
3585{
3586 if (!fSyncSetLoaded)
3587 return 510; // syncset should be loaded here!
3588 // search in syncset
3589 string localid;
3590 LOCALID_TO_STRING(aLocalID,localid)localid=aLocalID;
3591 TSyncSetList::iterator syncsetpos = findInSyncSet(localid.c_str());
3592 if (syncsetpos==fSyncSetList.end()) {
3593 // not found in current sync set, but could be a newly inserted item - try direct load
3594 // - create new empty TMultiFieldItem
3595 aItemP = (TMultiFieldItem *) newItemForRemote(ity_multifield);
3596 if (!aItemP) return 510;
3597 // - assign local id, as it is required e.g. by DoDataSubstitutions
3598 aItemP->setLocalID(localid.c_str());
3599 // - set default operation
3600 aItemP->setSyncOp(sop_replace);
3601 // - now fetch directly from DB
3602 localstatus sta = apiFetchItem(*((TMultiFieldItem *)aItemP),true,NULL__null);
3603 if (sta!=LOCERR_OK) {
3604 delete aItemP;
3605 aItemP = NULL__null;
3606 }
3607 return sta;
3608 }
3609 else {
3610 // return sync item from syncset item (fetches data now if not fetched before)
3611 return getItemFromSyncSetItem(*syncsetpos,aItemP);
3612 }
3613} // TCustomImplDS::getItemByID
3614
3615
3616/// update item by local ID in the sync set. Caller retains ownership of aItemP
3617/// @return != LOCERR_OK if item with specified ID is not found.
3618localstatus TCustomImplDS::updateItemByID(localid_t aLocalID, TSyncItem *aItemP)
3619{
3620 if (!aItemP) return 510; // error
3621 if (!aItemP->isBasedOn(ity_multifield)) return 415; // must be multifield item
3622 TMultiFieldItem *myItemP = static_cast<TMultiFieldItem *>(aItemP);
3623 // - assign localid
3624 string localid;
3625 LOCALID_TO_STRING(aLocalID,localid)localid=aLocalID;
3626 myItemP->setLocalID(localid.c_str());
3627 // have API handle it
3628 localstatus sta = apiUpdateItem(*myItemP);
3629 if (sta==LOCERR_OK) {
3630 // updated ok
3631 // - save what is needed for finalisation
3632 if (fNeedFinalisation) {
3633 queueForFinalisation(myItemP);
3634 }
3635 }
3636 return sta;
3637} // TCustomImplDS::updateItemByID
3638
3639
3640/// delete item by local ID in the sync set.
3641/// @return != LOCERR_OK if item with specified ID is not found.
3642localstatus TCustomImplDS::deleteItemByID(localid_t aLocalID)
3643{
3644 // create new dummy TMultiFieldItem
3645 TMultiFieldItem *myItemP =
3646 (TMultiFieldItem *) newItemForRemote(ity_multifield);
3647 // assign localid
3648 string localid;
3649 LOCALID_TO_STRING(aLocalID,localid)localid=aLocalID;
3650 myItemP->setLocalID(localid.c_str());
3651 // have API delete it
3652 localstatus sta = apiDeleteItem(*myItemP);
3653 delete myItemP; // delete dummy item
3654 // return status
3655 return sta;
3656} // TCustomImplDS::deleteItemByID
3657
3658
3659/// create new item in the sync set. Caller retains ownership of aItemP.
3660/// @return LOCERR_OK or error code.
3661/// @param[out] aNewLocalID local ID assigned to new item
3662/// @param[out] aReceiveOnly is set to true if local changes/deletion of this item should not be
3663/// reported to the server in normal syncs.
3664localstatus TCustomImplDS::createItem(TSyncItem *aItemP,localid_out_t &aNewLocalID, bool &aReceiveOnly)
3665{
3666 if (!aItemP) return 510; // error
3667 if (!aItemP->isBasedOn(ity_multifield)) return 415; // must be multifield item
3668 TMultiFieldItem *myItemP = static_cast<TMultiFieldItem *>(aItemP);
3669 // add it to the database
3670 string newLocalID;
3671 localstatus sta = apiAddItem(*myItemP,newLocalID);
3672 // return assigned ID
3673 STR_TO_LOCALID(newLocalID.c_str(),aNewLocalID)(aNewLocalID=(char *)newLocalID.c_str());
3674 if (sta==LOCERR_OK) {
3675 // added ok
3676 // - save what is needed for finalisation
3677 if (fNeedFinalisation) {
3678 myItemP->setLocalID(newLocalID.c_str()); // finalisation needs to know the local ID
3679 queueForFinalisation(myItemP);
3680 }
3681 }
3682 // so far, we don't have receive-only items
3683 aReceiveOnly = false;
3684 // return status
3685 return sta;
3686} // TCustomImplDS::createItem
3687
3688
3689/// zaps the entire datastore, returns LOCERR_OK if ok
3690/// @return LOCERR_OK or error code.
3691localstatus TCustomImplDS::zapDatastore(void)
3692{
3693 // make sure we have the sync set if we need it to zap it
3694 if (apiNeedSyncSetToZap()) {
3695 // make sure we have the sync set
3696 localstatus sta = makeSyncSetLoaded(false);
3697 if (sta!=LOCERR_OK)
3698 return sta; // error
3699 }
3700 // Zap the sync set in this datastore (will possibly call zapSyncSetOneByOne if there's no more efficient way to do it than one by one)
3701 // - make sure we have at least one pev_deleting event, in case app tracks it to see if session caused changes to DB
3702 DB_PROGRESS_EVENT(this,pev_deleting,0,0,0)this->getSession()->NotifySessionProgressEvent(pev_deleting
,this->getDSConfig(),0,0,0)
;
3703 // - now zap
3704 return apiZapSyncSet();
3705} // TCustomImplDS::zapDatastore
3706
3707
3708#endif // BASED_ON_BINFILE_CLIENT connecting methods
3709
3710
3711#ifdef DBAPI_TEXTITEMS1
3712
3713// helper to process params
3714// - if aParamName!=NULL, it searches for the value of the requested parameter and returns != NULL, NULL if none found
3715// - if aParamName==NULL, it scans until all params are skipped and returns end of params
3716cAppCharP paramScan(cAppCharP aParams,cAppCharP aParamName, string &aValue)
3717{
3718 cAppCharP p = aParams;
3719 cAppCharP q,r;
3720 int nl,vl;
3721 bool quotedvalue=false;
3722 if (!p) return NULL__null;
3723 while (*p && *p==';') {
3724 // skip param intro
3725 p++;
3726 // find end of param name
3727 for (q=p; *q!=0 && *q!=';' && *q!=':' && *q!='=';) q++;
3728 nl=q-p;
3729 // - now: p=start of name, nl=length of name
3730 // find end of param value
3731 if (nl && *q=='=') {
3732 // value starts after equal sign
3733 q++;
3734 if (*q=='"') { // " ) { work around bug in colorizer
3735 // quoted value
3736 quotedvalue=true;
3737 r=++q;
3738 while (*r && *r!='"') { // " ) { work around bug in colorizer
3739 if (*r=='\\') {
3740 r++;
3741 if (*r) r++;
3742 }
3743 else
3744 r++;
3745 }
3746 vl=r-q;
3747 if (*r) r++; // skip closing quote if not delimited by end of string
3748 }
3749 else {
3750 // unquoted value, ends at next colon, semicolon or line end (no value case)
3751 for (r=q; *r && *r!=':' && *r!=';' && *r!='\r' && *r!='\n';) r++;
3752 vl = r-q;
3753 }
3754 // - now: q=start of value, vl=length of value, *r=char after value
3755 }
3756 else {
3757 // no value
3758 r=q;
3759 vl=0;
3760 }
3761 // check if it's our value
3762 if (aParamName) {
3763 // we are searching a single parameter
3764 if (strucmp(p,aParamName,nl)==0) {
3765 // found, return it's value
3766 if (quotedvalue)
3767 CStrToStrAppend(q, aValue, true); // stop at quote or end of line
3768 else
3769 aValue.assign(q,vl);
3770 return p; // position of parameter name
3771 }
3772 }
3773 // next param
3774 p=r;
3775 }
3776 // end of all params
3777 if (aParamName) return NULL__null; // we were searching for a special param and haven't found it
3778 // we were scanning for the end of all params
3779 // - save all params
3780 aValue.assign(aParams,p-aParams);
3781 // - return pointer to what comes after params
3782 return p;
3783} // paramScan
3784
3785
3786// store API key/value pair field in named field
3787bool TCustomImplDS::storeField(
3788 cAppCharP aName,
3789 cAppCharP aParams,
3790 cAppCharP aValue,
3791 TMultiFieldItem &aItem,
3792 uInt16 aSetNo, // unused here in base class
3793 sInt16 aArrayIndex
3794)
3795{
3796 string s;
3797 // find field by name
3798 TItemField *fieldP = aItem.getArrayField(aName, aArrayIndex, false); // create element if not existing
3799 if (!fieldP) return false; // nothing stored
3800 // convert to app string
3801 s.erase();
3802 appendStringAsUTF8(aValue, s, chs_utf8, lem_cstr);
3803 // treat timestamp specially
3804 if (fieldP->isBasedOn(fty_timestamp)) {
3805 TTimestampField *tsfP = static_cast<TTimestampField *>(fieldP);
3806 // default time zone is none
3807 timecontext_t tctx = TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ));
3808 // modify time zone if params contain a TZNAME
3809 if (paramScan(aParams,"TZNAME",s)) {
3810 // convert to time zone context (olson allowed)
3811 TimeZoneNameToContext(s.c_str(), tctx, tsfP->getGZones(), true);
3812 }
3813 // now parse text string into field
3814 tsfP->setAsISO8601(aValue, tctx, false);
3815 }
3816 else {
3817 // all others: just set as string
3818 fieldP->setAsString(aValue);
3819 }
3820 return true;
3821} // TCustomImplDS::storeField
3822
3823
3824// - parse text data into item
3825// Note: generic implementation, using virtual storeField() method
3826// to differentiate between use with mapped fields in DBApi and
3827// direct (unmapped) TMultiFieldItem access in Tunnel API.
3828bool TCustomImplDS::parseItemData(
3829 TMultiFieldItem &aItem,
3830 cAppCharP aItemData,
3831 uInt16 aSetNo
3832)
3833{
3834 // read data from input string into mapped fields (or local vars)
3835 cAppCharP p = aItemData;
3836 cAppCharP q;
3837 string fieldname,params,value;
3838 bool readsomething=false;
3839 uInt16 arrayindex;
3840 // show item data as is
3841 PDEBUGPRINTFX(DBG_USERDATA+DBG_DBAPI+DBG_EXOTIC+DBG_HOT,("parseItemData received string from DBApi:")){ if (((0x01000000 +0x02000000 +0x80000000 +0x00000001) &
getDbgMask()) == (0x01000000 +0x02000000 +0x80000000 +0x00000001
)) getDbgLogger()->setNextMask(0x01000000 +0x02000000 +0x80000000
+0x00000001).DebugPrintfLastMask ("parseItemData received string from DBApi:"
); }
;
3842 PDEBUGPUTSXX(DBG_USERDATA+DBG_DBAPI+DBG_EXOTIC,aItemData,0,true){ if (((0x01000000 +0x02000000 +0x80000000) & getDbgMask(
)) == (0x01000000 +0x02000000 +0x80000000)) getDbgLogger()->
DebugPuts( 0x01000000 +0x02000000 +0x80000000,aItemData,0,true
); }
;
3843 // read all fields
3844 while(*p) {
3845 arrayindex=0;
3846 // find name
3847 for (q=p; *q && *q!='[' && *q!=':' && *q!=';';) q++;
3848 fieldname.assign(p,q-p);
3849 // check for array index
3850 if (*q=='[') {
3851 q++;
3852 q+=StrToUShort(q,arrayindex);
3853 if (*q==']') q++;
3854 }
3855 p=q;
3856 // find and skip params
3857 p = paramScan(p,NULL__null,params);
3858 // p should now point to ':'
3859 if (*p==':' || !params.empty()) { // blobs needn't to contain a ':'
3860 value.erase();
3861 if (*p==':') { // only get a value, if there is one !!
3862 p++; // consume colon
3863 // get value
3864 p += CStrToStrAppend( p,value,true ); // stop at quote or ctrl char
3865 } // if
3866 // store field now
3867 if (storeField(
3868 fieldname.c_str(),
3869 params.c_str(),
3870 value.c_str(),
3871 aItem,
3872 aSetNo, // ordering of params is correct now ( before <arrayindex> !! )
3873 arrayindex
3874 ))
3875 readsomething=true;
3876 }
3877 // skip everything up to next end of line (in case value was terminated by a quote or other ctrl char)
3878 while (*p && *p!='\r' && *p!='\n') p++;
3879 // skip all line end chars up to beginning of next line or end of record
3880 while (*p && (*p=='\r' || *p=='\n')) p++;
3881 // p now points to next line's beginning
3882 };
3883 return readsomething;
3884} // TCustomImplDS::parseItemData
3885
3886
3887
3888
3889// generate text representation of a single item field
3890bool TCustomImplDS::generateItemFieldData(
3891 bool aAssignedOnly,
3892 TCharSets aDataCharSet,
3893 TLineEndModes aDataLineEndMode,
3894 timecontext_t aTimeContext,
3895 TItemField *aBasefieldP,
3896 cAppCharP aBaseFieldName,
3897 string &aDataFields
3898)
3899{
3900 TItemField *leaffieldP;
3901 string val;
3902
3903 if (!aBasefieldP) return false;
3904 // ignore field if it is not assigned and assignedonly flag is set
3905 if (aAssignedOnly && aBasefieldP->isUnassigned()) return false;
3906 // yes, we want to write this field
3907 #ifdef ARRAYFIELD_SUPPORT1
3908 uInt16 arrayIndex=0;
3909 #endif
3910 do {
3911 // first check if there is an element at all
3912 #ifdef ARRAYFIELD_SUPPORT1
3913 if (aBasefieldP->isArray())
3914 leaffieldP = aBasefieldP->getArrayField(arrayIndex,true); // get existing leaf fields only
3915 else
3916 leaffieldP = aBasefieldP; // leaf is base field
3917 #else
3918 leaffieldP = aBasefieldP; // leaf is base field
3919 #endif
3920 // if no leaf field, we'll need to exit here (we're done with the array)
3921 if (leaffieldP==NULL__null) break;
3922 // we have some data, first append name
3923 aDataFields += aBaseFieldName;
3924 #ifdef ARRAYFIELD_SUPPORT1
3925 // append array index if this is an array field
3926 if (aBasefieldP->isArray())
3927 StringObjAppendPrintf(aDataFields,"[%d]",arrayIndex);
3928 #endif
3929 // append value
3930 if (aBasefieldP->elementsBasedOn(fty_blob)) {
3931 // - for blobs we use a BlobID and send the data later
3932 aDataFields += ";BLOBID=";
3933 aDataFields += aBaseFieldName;
3934 #ifdef ARRAYFIELD_SUPPORT1
3935 // append array index if this is an array field
3936 if (aBasefieldP->isArray())
3937 StringObjAppendPrintf(aDataFields,"[%d]",arrayIndex);
3938 #endif
3939 }
3940 else {
3941 // - literal value (converted to DB charset as C-escaped string)
3942 if (leaffieldP->isBasedOn(fty_timestamp)) {
3943 TTimestampField *tsfP = static_cast<TTimestampField *>(leaffieldP);
3944 // get original zone
3945 timecontext_t tctx = tsfP->getTimeContext();
3946 if (TCTX_IS_DURATION(tctx) || TCTX_IS_DATEONLY(tctx) ||!TCTX_IS_UNKNOWN(tctx)) {
3947 // not fully floating, get name
3948 TimeZoneContextToName(tctx, val, tsfP->getGZones());
3949 // append it
3950 aDataFields+= ";TZNAME=";
3951 aDataFields+= val;
3952 }
3953 // now convert to database time zone
3954 tctx = aTimeContext; // desired output zone
3955 // report as-is if we have a floating map or if it IS floating
3956 if (tsfP->isFloating())
3957 tctx = TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)); // report as-is
3958 // now create ISO8601 representation in the requested output time zone
3959 // Note: unless output time zone is "FLOATING", this timestamp is *not* in the time zone
3960 // of TZNAME, but normalized to the requested output time zone!
3961 tsfP->getAsISO8601(val,tctx,true,false,false);
3962 }
3963 else {
3964 leaffieldP->getAsString(val); // get value
3965 }
3966 aDataFields+=':'; // delimiter
3967 string valDB;
3968 appendUTF8ToString(
3969 val.c_str(),
3970 valDB,
3971 aDataCharSet,
3972 aDataLineEndMode
3973 );
3974 StrToCStrAppend(valDB.c_str(),aDataFields,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
3975 } // if
3976 aDataFields+="\r\n"; // CRLF at end
3977 // next item in array
3978 #ifdef ARRAYFIELD_SUPPORT1
3979 arrayIndex++;
3980 #endif
3981 } while(aBasefieldP->isArray()); // only arrays do loop
3982 // generated something
3983 return true;
3984} // TCustomImplDS::generateItemFieldData
3985
3986
3987#ifdef DBAPI_TUNNEL_SUPPORT
3988
3989// - parse itemdata into item using DB mappings
3990bool TCustomImplDS::parseTunnelItemData(
3991 TMultiFieldItem &aItem,
3992 cAppCharP aItemData
3993)
3994{
3995 return parseItemData(aItem, aItemData, 0); // internal fields don't have set numbers
3996} // TCustomImplDS::parseTunnelItemData
3997
3998
3999// generate text representations of item's fields (BLOBs and parametrized fields not included)
4000// - returns true if at least one field appended
4001bool TCustomImplDS::generateTunnelItemData(
4002 bool aAssignedOnly,
4003 TMultiFieldItem *aItemP,
4004 string &aDataFields
4005)
4006{
4007 bool createdone=false;
4008
4009 // create text representation for all fields in the field list
4010 TFieldListConfig *flcP = aItemP->getFieldDefinitions();
4011 sInt16 fid = 0;
4012 while (fid<flcP->numFields()) {
4013 // get base field
4014 TItemField *basefieldP = aItemP->getField(fid);
4015 // The plugin api docs say that text items should be UTF8 with CRLF line ends.
4016 // Plugins might choose how they render their timestamps, we choose UTC (so rendered timestamps will have trailing Z,
4017 // unless they are really floating). Note that the TZNAME param still indicates the originating timezone (so is NOT
4018 // always UTC!).
4019 if (generateItemFieldData(aAssignedOnly, chs_utf8, lem_dos, TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)), basefieldP, TCFG_CSTR(flcP->fFields[fid].fieldname)flcP->fFields[fid].fieldname.c_str(), aDataFields))
4020 createdone=true; // we now have at least one field
4021 fid++;
4022 } // for all field mappings
4023 PDEBUGPRINTFX(DBG_USERDATA+DBG_EXOTIC+DBG_HOT,("generateItemData generated string for TunnelAPI:")){ if (((0x01000000 +0x80000000 +0x00000001) & getDbgMask(
)) == (0x01000000 +0x80000000 +0x00000001)) getDbgLogger()->
setNextMask(0x01000000 +0x80000000 +0x00000001).DebugPrintfLastMask
("generateItemData generated string for TunnelAPI:"); }
;
4024 PDEBUGPUTSXX(DBG_USERDATA+DBG_EXOTIC,aDataFields.c_str(),0,true){ if (((0x01000000 +0x80000000) & getDbgMask()) == (0x01000000
+0x80000000)) getDbgLogger()->DebugPuts( 0x01000000 +0x80000000
,aDataFields.c_str(),0,true); }
;
4025 return createdone;
4026} // TCustomImplDS::generateTunnelItemData
4027
4028#endif // DBAPI_TUNNEL_SUPPORT
4029
4030#endif // DBAPI_TEXTITEMS
4031
4032
4033
4034#if defined(DBAPI_ASKEYITEMS1) && defined(ENGINEINTERFACE_SUPPORT1)
4035
4036
4037// - get a settings key instance that can access the item
4038// NULL is allowed for aItemP for cases where we don't have or want an item (!ReadNextItem:allfields)
4039TDBItemKey *TCustomImplDS::newDBItemKey(TMultiFieldItem *aItemP, bool aOwnsItem)
4040{
4041 return new TDBItemKey(getSession()->getSyncAppBase()->fEngineInterfaceP,aItemP,this,aOwnsItem);
4042} // TCustomImplDS::newDBItemKey
4043
4044
4045// TDBItemKey
4046// ==========
4047
4048
4049// set new content item
4050void TDBItemKey::setItem(TMultiFieldItem *aItemP, bool aPassOwner)
4051{
4052 forgetItem();
4053 fItemP = aItemP;
4054 fOwnsItem = aPassOwner;
4055 fWritten = fItemP; // if we have set an item, this counts as written
4056} // TDBItemKey::setItem
4057
4058
4059// get FID for specified name
4060sInt16 TDBItemKey::getFidFor(cAppCharP aName, stringSize aNameSz)
4061{
4062 if (!fItemP) return VARIDX_UNDEFINED-128; // no item, no field is accessible
4063
4064 TFieldMapList *fmlP = &(fCustomImplDS->fConfigP->fFieldMappings.fFieldMapList);
4065
4066 // check for iterator commands first
4067 if (strucmp(aName,VALNAME_FIRST".FIRST")==0) {
4068 fIterator=fmlP->begin();
4069 if (fIterator!=fmlP->end())
4070 return static_cast<TFieldMapItem *>(*fIterator)->fid;
4071 }
4072 else if (strucmp(aName,VALNAME_NEXT".NEXT")==0) {
4073 if (fIterator!=fmlP->end())
4074 fIterator++;
4075 if (fIterator!=fmlP->end())
4076 return static_cast<TFieldMapItem *>(*fIterator)->fid;
4077 }
4078 else {
4079 TFieldMapList::iterator pos;
4080 for (pos=fmlP->begin(); pos!=fmlP->end(); pos++) {
4081 // check for name
4082 TFieldMapItem *fmiP = static_cast<TFieldMapItem *>(*pos);
4083 if (strucmp(aName,fmiP->getName(),aNameSz)==0) {
4084 // return field ID (negative = local script var, positive = item field)
4085 return fmiP->fid;
4086 }
4087 }
4088 }
4089 // none found
4090 return VARIDX_UNDEFINED-128;
4091} // TDBItemKey::getFidFor
4092
4093
4094
4095TItemField *TDBItemKey::getBaseFieldFromFid(sInt16 aFid)
4096{
4097 if (!fItemP) return NULL__null; // no item, no field is accessible
4098 return fCustomImplDS->getMappedBaseFieldOrVar(*fItemP, aFid);
4099} // TDBItemKey::getBaseFieldFromFid
4100
4101
4102bool TDBItemKey::getFieldNameFromFid(sInt16 aFid, string &aFieldName)
4103{
4104 if (!fItemP) return false; // no item, no field is accessible
4105 // name is map name (NOT field name!)
4106 TFieldMapList *fmlP = &(fCustomImplDS->fConfigP->fFieldMappings.fFieldMapList);
4107 TFieldMapList::iterator pos;
4108 TFieldMapItem *fmiP;
4109
4110 // search field map item by fid, return name
4111 for (pos=fmlP->begin(); pos!=fmlP->end(); pos++) {
4112 fmiP = static_cast<TFieldMapItem *>(*pos);
4113 // check for fid
4114 if (fmiP->fid == aFid) {
4115 // return name
4116 aFieldName = fmiP->getName();
4117 return true;
4118 }
4119 }
4120 // none found
4121 return false;
4122} // TDBItemKey::getFieldNameFromFid
4123
4124
4125#endif // DBAPI_ASKEYITEMS and ENGINEINTERFACE_SUPPORT
4126
4127
4128// Tunnel DB API (accessing a datastore from UIAPI) within a tunnel session
4129// ------------------------------------------------------------------------
4130
4131
4132#ifdef DBAPI_TUNNEL_SUPPORT
4133
4134// private helper preparing type infrastucture so we can use it
4135void TCustomImplDS::setupTunnelTypes(TSyncItemType *aItemTypeP)
4136{
4137 // make sure we have types, or set type if we explicitly specify one
4138 if (!canCreateItemForRemote() || aItemTypeP) {
4139 // default to preferred TX type if none specified
4140 if (!aItemTypeP) aItemTypeP = getPreferredTxItemType();
4141 // install single type for everything
4142 setSendTypeInfo(aItemTypeP,aItemTypeP);
4143 setReceiveTypeInfo(aItemTypeP,aItemTypeP);
4144 }
4145}
4146
4147
4148
4149
4150// must be called to start accesses (read or write)
4151TSyError TCustomImplDS::TunnelStartDataRead(cAppCharP lastToken, cAppCharP resumeToken)
4152{
4153 TSyError sta = LOCERR_OK;
4154
4155 // forget previously started stuff
4156 InternalResetDataStore();
4157 // force reading all data (we are simulating a plugin, which always reports entire sync set!)
4158 fSlowSync = true;
4159 // make admin ready
4160 string deviceID = "tunnelSession_";
4161 deviceID += getName(); // append datastore name to build pseudo device name
4162 sta = implMakeAdminReady(deviceID.c_str(), getName(), "tunnelDBAPI");
4163 if (sta==LOCERR_OK) {
4164 // setup types - default to preferred tx if not explicitly set (via /tunnel/itemtype)
4165 setupTunnelTypes();
4166 // make sure types are ready for use
4167 initDataTypeUse();
4168 // start anew
4169 sta = implStartDataRead();
4170 }
4171 return sta;
4172}
4173
4174
4175
4176TSyError TCustomImplDS::TunnelReadNextItemInternal(ItemID aID, TSyncItem *&aItemP, sInt32 *aStatus, bool aFirst)
4177{
4178 TSyError sta = LOCERR_OK;
4179 // rewind if first item requested
4180 if (aFirst) {
4181 sta = implStartDataRead();
4182 }
4183 // get next item from DB
4184 if (sta==LOCERR_OK) {
4185 bool isEOF;
4186 bool changed = false; // report all items, not only changed ones
4187 aItemP = NULL__null;
4188 sta = implGetItem(isEOF, changed, aItemP);
4189 if (sta==LOCERR_OK) {
4190 if (isEOF) {
4191 // no item
4192 *aStatus = ReadNextItem_EOF;
4193 }
4194 else {
4195 // item found
4196 // Note: changed status is not really reliable, does not differentiate resumed/normal
4197 // and does not relate to tokens passed in TunnelStartDataRead().
4198 // It reflects what the next normal sync would report as changed
4199 *aStatus = changed ? ReadNextItem_Changed : ReadNextItem_Unchanged;
4200 // implGetItem should deliver some data in all cases
4201 if (!aItemP) return DB_Error; // something's wrong
4202 // get ID
4203 aID->item = StrAlloc(aItemP->getLocalID());
4204 aID->parent = NULL__null; // none
4205 }
4206 }
4207 }
4208 return sta;
4209} // TCustomImplDS::TunnelReadNextItemInternal
4210
4211
4212TSyError TCustomImplDS::TunnelReadNextItem(ItemID aID, appCharP *aItemData, sInt32 *aStatus, bool aFirst)
4213{
4214 TSyncItem *itemP = NULL__null;
4215 *aItemData = NULL__null;
4216 TSyError sta = TunnelReadNextItemInternal(aID,itemP,aStatus,aFirst);
4217 if (itemP) {
4218 if (aItemData) {
4219 // create text version and return it
4220 string textData;
4221 textData.erase();
4222 generateTunnelItemData(false,static_cast<TMultiFieldItem *>(itemP),textData);
4223 *aItemData = StrAlloc(textData.c_str());
4224 }
4225 // not used any more, we have the text representation
4226 delete itemP;
4227 }
4228 return sta;
4229}
4230
4231
4232TSyError TCustomImplDS::TunnelReadNextItemAsKey(ItemID aID, KeyH aItemKey, sInt32 *aStatus, bool aFirst)
4233{
4234 TSyncItem *itemP = NULL__null;
4235 TSyError sta = TunnelReadNextItemInternal(aID,itemP,aStatus,aFirst);
4236 if (itemP) {
4237 // assign data
4238 if (aItemKey) {
4239 // pass the item to the key and let key own it, so item will be deleted with key
4240 reinterpret_cast<TMultiFieldItemKey *>(aItemKey)->setItem(static_cast<TMultiFieldItem *>(itemP), true);
4241 }
4242 else {
4243 // nobody wants the item, delete it
4244 delete itemP;
4245 }
4246 }
4247 return sta;
4248} // TCustomImplDS::TunnelReadNextItemAsKey
4249
4250
4251
4252TSyError TCustomImplDS::TunnelReadItem(cItemID aID, appCharP *aItemData)
4253{
4254 *aItemData = NULL__null;
4255 // create empty item
4256 TMultiFieldItem *itemP = static_cast<TMultiFieldItem *>(newItemForRemote(ity_multifield));
4257 // set localID to retrieve
4258 itemP->setLocalID(aID->item);
4259 // retrieve
4260 TStatusCommand dummy(getSession());
4261 TSyError sta = implRetrieveItemByID(*itemP, dummy) ? LOCERR_OK : dummy.getStatusCode();
4262 if (sta==LOCERR_OK) {
4263 if (aItemData) {
4264 // create text version and return it
4265 string textData;
4266 textData.erase();
4267 generateTunnelItemData(false,static_cast<TMultiFieldItem *>(itemP),textData);
4268 *aItemData = StrAlloc(textData.c_str());
4269 }
4270 // not used any more, we have the text representation
4271 delete itemP;
4272 }
4273 return sta;
4274}
4275
4276
4277TSyError TCustomImplDS::TunnelReadItemAsKey(cItemID aID, KeyH aItemKey)
4278{
4279 // get item
4280 TMultiFieldItem *itemP = reinterpret_cast<TMultiFieldItemKey *>(aItemKey)->getItem();
4281 // set localID to retrieve
4282 itemP->setLocalID(aID->item);
4283 // retrieve
4284 TStatusCommand dummy(getSession());
4285 return implRetrieveItemByID(*itemP, dummy) ? LOCERR_OK : dummy.getStatusCode();
4286} // TCustomImplDS::TunnelReadItemAsKey
4287
4288
4289
4290// end of accessing sync set (single item retrieval still possible)
4291TSyError TCustomImplDS::TunnelEndDataRead()
4292{
4293 // just pass on
4294 return implEndDataRead();
4295} // TCustomImplDS::TunnelEndDataRead
4296
4297
4298
4299TSyError TCustomImplDS::TunnelStartDataWrite()
4300{
4301 // just pass on
4302 return apiStartDataWrite();
4303} // TCustomImplDS::TunnelStartDataWrite
4304
4305
4306
4307// helper routine for insert
4308TSyError TCustomImplDS::TunnelInsertItemInternal(TMultiFieldItem *aItemP, ItemID aNewID)
4309{
4310 string newid;
4311 TSyError sta = apiAddItem(*aItemP, newid);
4312 if (sta==LOCERR_OK) {
4313 if (aNewID) {
4314 aNewID->item = StrAlloc(newid.c_str());
4315 aNewID->parent = NULL__null; // none
4316 }
4317 }
4318 return sta;
4319} // TCustomImplDS::TunnelInsertItemInternal
4320
4321
4322TSyError TCustomImplDS::TunnelInsertItem(cAppCharP aItemData, ItemID aID)
4323{
4324 TMultiFieldItem *itemP = static_cast<TMultiFieldItem *>(newItemForRemote(ity_multifield));
4325 TSyError sta = LOCERR_WRONGUSAGE; // no parseable data
4326 if (parseItemData(*itemP, aItemData, 0)) {
4327 // parsed some data, insert it
4328 sta = TunnelInsertItemInternal(itemP,aID);
4329 }
4330 // delete the item
4331 delete itemP;
4332 return sta;
4333} // TCustomImplDS::TunnelInsertItem
4334
4335
4336TSyError TCustomImplDS::TunnelInsertItemAsKey(KeyH aItemKey, ItemID aID)
4337{
4338 return TunnelInsertItemInternal(reinterpret_cast<TMultiFieldItemKey *>(aItemKey)->getItem(),aID);
4339} // TCustomImplDS::TunnelInsertItemAsKey
4340
4341
4342
4343// helper routine for update
4344TSyError TCustomImplDS::TunnelUpdateItemInternal(TMultiFieldItem *aItemP, cItemID aID, ItemID aUpdID)
4345{
4346 string updid;
4347 aItemP->setLocalID(aID->item);
4348 TSyError sta = apiUpdateItem(*aItemP);
4349 if (sta==LOCERR_OK) {
4350 if (aUpdID) {
4351 cAppCharP newID = aItemP->getLocalID();
4352 if (strcmp(newID,aID->item)!=0)
4353 aUpdID->item = StrAlloc(newID);
4354 else
4355 aUpdID->item = NULL__null;
4356 aUpdID->parent = NULL__null; // none
4357 }
4358 }
4359 return sta;
4360} // TCustomImplDS::TunnelUpdateItemInternal
4361
4362
4363TSyError TCustomImplDS::TunnelUpdateItem(cAppCharP aItemData, cItemID aID, ItemID aUpdID)
4364{
4365 TMultiFieldItem *itemP = static_cast<TMultiFieldItem *>(newItemForRemote(ity_multifield));
4366 TSyError sta = LOCERR_WRONGUSAGE; // no parseable data
4367 if (parseItemData(*itemP, aItemData, 0)) {
4368 // parsed some data, insert it
4369 sta = TunnelUpdateItemInternal(itemP,aID,aUpdID);
4370 }
4371 // delete the item
4372 delete itemP;
4373 return sta;
4374} // TCustomImplDS::TunnelUpdateItem
4375
4376
4377TSyError TCustomImplDS::TunnelUpdateItemAsKey(KeyH aItemKey, cItemID aID, ItemID aUpdID)
4378{
4379 return TunnelUpdateItemInternal(reinterpret_cast<TMultiFieldItemKey *>(aItemKey)->getItem(),aID,aUpdID);
4380} // TCustomImplDS::TunnelUpdateItemAsKey
4381
4382
4383
4384TSyError TCustomImplDS::TunnelMoveItem(cItemID aID, cAppCharP newParID)
4385{
4386 return LOCERR_NOTIMP;
4387} // TCustomImplDS::TunnelMoveItem
4388
4389
4390
4391TSyError TCustomImplDS::TunnelDeleteItem(cItemID aID)
4392{
4393 TMultiFieldItem *itemP = static_cast<TMultiFieldItem *>(newItemForRemote(ity_multifield));
4394 itemP->setLocalID(aID->item);
4395 TSyError sta = apiDeleteItem(*itemP);
4396 delete itemP;
4397 return sta;
4398} // TCustomImplDS::TunnelDeleteItem
4399
4400
4401
4402TSyError TCustomImplDS::TunnelEndDataWrite(bool aSuccess, appCharP *aNewToken)
4403{
4404 string newToken;
4405 TSyError sta = apiEndDataWrite(newToken);
4406 if (aNewToken) {
4407 *aNewToken = StrAlloc(newToken.c_str());
4408 }
4409 return sta;
4410} // TCustomImplDS::TunnelEndDataWrite
4411
4412
4413
4414void TCustomImplDS::TunnelDisposeObj(void* aMemory)
4415{
4416 // return string we have created as a plugin (is NULL safe)
4417 StrDispose(aMemory);
4418} // TCustomImplDS::TunnelDisposeObj
4419
4420
4421
4422// Tunnel key factory method
4423TSettingsKeyImpl *TCustomImplDS::newTunnelKey(TEngineInterface *aEngineInterfaceP)
4424{
4425 return new TCustomDSTunnelKey(aEngineInterfaceP,this);
4426} // TCustomImplDS::newTunnelKey
4427
4428
4429
4430// TTunnelKey
4431// ----------
4432
4433// constructor
4434TCustomDSTunnelKey::TCustomDSTunnelKey(
4435 TEngineInterface *aEngineInterfaceP,
4436 TCustomImplDS *aCustomImplDsP
4437) :
4438 inherited(aEngineInterfaceP),
4439 fCustomImplDsP(aCustomImplDsP)
4440{
4441} // TCustomDSTunnelKey::TCustomDSTunnelKey
4442
4443
4444// destructor - close key
4445TCustomDSTunnelKey::~TCustomDSTunnelKey()
4446{
4447 // closing key
4448} // TCustomDSTunnelKey::~TCustomDSTunnelKey
4449
4450
4451// open subkey by name (not by path!)
4452// - this is the actual implementation
4453TSyError TCustomDSTunnelKey::OpenSubKeyByName(
4454 TSettingsKeyImpl *&aSettingsKeyP,
4455 cAppCharP aName, stringSize aNameSize,
4456 uInt16 aMode
4457) {
4458 if (strucmp(aName,"item",aNameSize)==0) {
4459 // make sure defaults are initialized
4460 fCustomImplDsP->setupTunnelTypes();
4461 // create a sendable item
4462 TMultiFieldItem *itemP = static_cast<TMultiFieldItem *>(fCustomImplDsP->newItemForRemote(ity_multifield));
4463 // wrap it into a item key
4464 aSettingsKeyP = new TMultiFieldItemKey(fEngineInterfaceP, itemP, true); // item is owned by key, which means that it will be deleted with key
4465 // done
4466 return LOCERR_OK;
4467 }
4468 else
4469 return inherited::OpenSubKeyByName(aSettingsKeyP,aName,aNameSize,aMode);
4470 // opened a key
4471 return LOCERR_OK;
4472} // TCustomDSTunnelKey::OpenSubKeyByName
4473
4474
4475// - get item type name (if not written before, this will be the preferred type of the datastore)
4476static TSyError readItemType(
4477 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
4478 appPointer aBuffer, memSize aBufSize, memSize &aValSize
4479)
4480{
4481 TCustomImplDS *ds = static_cast<TCustomDSTunnelKey *>(aStructFieldsKeyP)->getCustomImplDs();
4482 // make sure defaults are initialized
4483 ds->setupTunnelTypes();
4484 // get name of fLocalSendToRemoteTypeP
4485 return TStructFieldsKey::returnString(ds->getLocalSendType()->getTypeName(), aBuffer, aBufSize, aValSize);
4486} // readItemType
4487
4488
4489
4490// - set item type
4491static TSyError writeItemType(
4492 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
4493 cAppPointer aBuffer, memSize aValSize
4494)
4495{
4496 TCustomImplDS *ds = static_cast<TCustomDSTunnelKey *>(aStructFieldsKeyP)->getCustomImplDs();
4497 cAppCharP p=cAppCharP(aBuffer);
4498 TSyncItemType *ty = ds->getSendType(p,NULL__null);
4499 if (!ty) return 404; // type not found
4500 ds->setupTunnelTypes(ty);
4501 // done
4502 return LOCERR_OK;
4503} // writeItemType
4504
4505
4506// accessor table for tunnel key values
4507const TStructFieldInfo TunnelFieldInfos[] =
4508{
4509 // valName, valType, writable, fieldOffs, valSiz
4510 { "itemtype", VALTYPE_TEXT, true, 0, 0, &readItemType, &writeItemType },
4511};
4512const sInt32 numTunnelFieldInfos = sizeof(TunnelFieldInfos)/sizeof(TStructFieldInfo);
4513
4514
4515// get table describing the fields in the struct
4516const TStructFieldInfo *TCustomDSTunnelKey::getFieldsTable(void)
4517{
4518 return TunnelFieldInfos;
4519} // TCustomDSTunnelKey::getFieldsTable
4520
4521sInt32 TCustomDSTunnelKey::numFields(void)
4522{
4523 return numTunnelFieldInfos;
4524} // TCustomDSTunnelKey::numFields
4525
4526
4527
4528#endif // DBAPI_TUNNEL_SUPPORT
4529
4530/* end of TCustomImplDS implementation */
4531
4532} // namespace
4533
4534// eof