@itwin/imodel-transformer 2.0.0-dev.1 → 2.0.0-dev.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
  3. package/lib/cjs/BranchProvenanceInitializer.js +11 -13
  4. package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
  5. package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +1 -1
  6. package/lib/cjs/DetachedExportElementAspectsStrategy.js +5 -5
  7. package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +1 -1
  8. package/lib/cjs/ECReferenceTypesCache.d.ts +3 -0
  9. package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
  10. package/lib/cjs/ECReferenceTypesCache.js +103 -40
  11. package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
  12. package/lib/cjs/ElementCascadingDeleter.js +1 -1
  13. package/lib/cjs/ElementCascadingDeleter.js.map +1 -1
  14. package/lib/cjs/EntityUnifier.d.ts +1 -1
  15. package/lib/cjs/EntityUnifier.d.ts.map +1 -1
  16. package/lib/cjs/EntityUnifier.js +5 -3
  17. package/lib/cjs/EntityUnifier.js.map +1 -1
  18. package/lib/cjs/ExportElementAspectsStrategy.d.ts +2 -2
  19. package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +1 -1
  20. package/lib/cjs/ExportElementAspectsStrategy.js.map +1 -1
  21. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +2 -2
  22. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +1 -1
  23. package/lib/cjs/IModelCloneContext.d.ts +3 -3
  24. package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
  25. package/lib/cjs/IModelCloneContext.js +58 -52
  26. package/lib/cjs/IModelCloneContext.js.map +1 -1
  27. package/lib/cjs/IModelExporter.d.ts +16 -7
  28. package/lib/cjs/IModelExporter.d.ts.map +1 -1
  29. package/lib/cjs/IModelExporter.js +105 -96
  30. package/lib/cjs/IModelExporter.js.map +1 -1
  31. package/lib/cjs/IModelImporter.d.ts +2 -2
  32. package/lib/cjs/IModelImporter.d.ts.map +1 -1
  33. package/lib/cjs/IModelImporter.js +16 -15
  34. package/lib/cjs/IModelImporter.js.map +1 -1
  35. package/lib/cjs/IModelTransformer.d.ts +54 -45
  36. package/lib/cjs/IModelTransformer.d.ts.map +1 -1
  37. package/lib/cjs/IModelTransformer.js +445 -431
  38. package/lib/cjs/IModelTransformer.js.map +1 -1
  39. package/lib/cjs/TransformerLoggerCategory.d.ts +6 -5
  40. package/lib/cjs/TransformerLoggerCategory.d.ts.map +1 -1
  41. package/lib/cjs/TransformerLoggerCategory.js +6 -5
  42. package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
  43. package/lib/cjs/imodel-transformer.js +2 -2
  44. package/lib/cjs/imodel-transformer.js.map +1 -1
  45. package/package.json +33 -33
@@ -77,6 +77,15 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
77
77
  targetDb;
78
78
  /** The IModelTransformContext for this IModelTransformer. */
79
79
  context;
80
+ /** The transform to be applied to the placement of spatial elements
81
+ * This transform should be applied when:
82
+ * - source and target db have different ECEF locations
83
+ * - source and target db have matching GCS/CRS data, but differing `geographicCoordinateSystem.additionalTransform.helmert2DWithZOffset`
84
+ * @note for ECEF transforms, this can only be used when source and target are linearly located imodels
85
+ * @note for non linearly located imodels, this transform will be a linear transform derived from Helmert Transforms from the src and target iModels.
86
+ * @beta
87
+ */
88
+ _linearSpatialTransform;
80
89
  _syncType;
81
90
  /** The Id of the Element in the **target** iModel that represents the **source** repository as a whole and scopes its [ExternalSourceAspect]($backend) instances. */
82
91
  get targetScopeElementId() {
@@ -101,7 +110,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
101
110
  * @param dbToQuery db to run the query on for scope external source
102
111
  * @param aspectProps aspectProps to search for @see ExternalSourceAspectProps
103
112
  */
104
- static queryScopeExternalSourceAspect(dbToQuery, aspectProps) {
113
+ static async queryScopeExternalSourceAspect(dbToQuery, aspectProps) {
105
114
  const sql = `
106
115
  SELECT ECInstanceId, Version, JsonProperties
107
116
  FROM ${core_backend_1.ExternalSourceAspect.classFullName}
@@ -111,7 +120,21 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
111
120
  AND Identifier=:identifier
112
121
  LIMIT 1
113
122
  `;
114
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
123
+ // if (aspectProps.scope === undefined) return undefined;
124
+ // const params = new QueryBinder();
125
+ // params.bindId("elementId", aspectProps.element.id);
126
+ // params.bindId("scopeId", aspectProps.scope.id);
127
+ // params.bindString("kind", aspectProps.kind);
128
+ // params.bindString("identifier", aspectProps.identifier);
129
+ // const reader = dbToQuery.createQueryReader(sql, params, {usePrimaryConn: true});
130
+ // if (await reader.step()) {
131
+ // const aspectId = reader.current.id;
132
+ // const version = reader.current.version as string | undefined;
133
+ // const jsonProperties = reader.current.jsonProperties as string | undefined;
134
+ // return { aspectId, version, jsonProperties };
135
+ // }
136
+ // return undefined;
137
+ // eslint-disable-next-line @itwin/no-internal, @typescript-eslint/no-deprecated
115
138
  return dbToQuery.withPreparedStatement(sql, (statement) => {
116
139
  statement.bindId("elementId", aspectProps.element.id);
117
140
  if (aspectProps.scope === undefined)
@@ -140,7 +163,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
140
163
  * @throws if no scoping ESA can be found in either the sourceDb or targetDb which describes a master branch relationship between the two databases.
141
164
  * @returns "forward" or "reverse"
142
165
  */
143
- static determineSyncType(sourceDb, targetDb,
166
+ static async determineSyncType(sourceDb, targetDb,
144
167
  /** @see [[IModelTransformOptions.targetScopeElementId]] */
145
168
  targetScopeElementId) {
146
169
  const aspectProps = {
@@ -157,19 +180,19 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
157
180
  jsonProperties: undefined,
158
181
  };
159
182
  /** First check if the targetDb is the branch (branch is the @see provenanceDb) */
160
- const esaPropsFromTargetDb = this.queryScopeExternalSourceAspect(targetDb, aspectProps);
183
+ const esaPropsFromTargetDb = await this.queryScopeExternalSourceAspect(targetDb, aspectProps);
161
184
  if (esaPropsFromTargetDb !== undefined) {
162
185
  return "forward"; // we found an esa assuming targetDb is the provenanceDb/branch so this is a forward sync.
163
186
  }
164
187
  // Now check if the sourceDb is the branch
165
188
  aspectProps.identifier = targetDb.iModelId;
166
- const esaPropsFromSourceDb = this.queryScopeExternalSourceAspect(sourceDb, aspectProps);
189
+ const esaPropsFromSourceDb = await this.queryScopeExternalSourceAspect(sourceDb, aspectProps);
167
190
  if (esaPropsFromSourceDb !== undefined) {
168
191
  return "reverse"; // we found an esa assuming sourceDb is the provenanceDb/branch so this is a reverse sync.
169
192
  }
170
193
  throw new Error(this.noEsaSyncDirectionErrorMessage);
171
194
  }
172
- determineSyncType() {
195
+ async determineSyncType() {
173
196
  if (this._isProvenanceInitTransform) {
174
197
  return "forward";
175
198
  }
@@ -177,7 +200,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
177
200
  return "not-sync";
178
201
  }
179
202
  try {
180
- return IModelTransformer.determineSyncType(this.sourceDb, this.targetDb, this.targetScopeElementId);
203
+ return await IModelTransformer.determineSyncType(this.sourceDb, this.targetDb, this.targetScopeElementId);
181
204
  }
182
205
  catch (err) {
183
206
  if (err instanceof Error &&
@@ -188,14 +211,14 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
188
211
  throw err;
189
212
  }
190
213
  }
191
- get isReverseSynchronization() {
214
+ async getIsReverseSynchronization() {
192
215
  if (this._syncType === undefined)
193
- this._syncType = this.determineSyncType();
216
+ this._syncType = await this.determineSyncType();
194
217
  return this._syncType === "reverse";
195
218
  }
196
- get isForwardSynchronization() {
219
+ async getIsForwardSynchronization() {
197
220
  if (this._syncType === undefined)
198
- this._syncType = this.determineSyncType();
221
+ this._syncType = await this.determineSyncType();
199
222
  return this._syncType === "forward";
200
223
  }
201
224
  _changesetRanges = undefined;
@@ -230,10 +253,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
230
253
  // non-falsy defaults
231
254
  cloneUsingBinaryGeometry: options?.cloneUsingBinaryGeometry ?? true,
232
255
  targetScopeElementId: options?.targetScopeElementId ?? core_common_1.IModel.rootSubjectId,
233
- // eslint-disable-next-line deprecation/deprecation
234
256
  danglingReferencesBehavior: options?.danglingReferencesBehavior ?? "reject",
235
257
  branchRelationshipDataBehavior: options?.branchRelationshipDataBehavior ?? "reject",
236
258
  skipPropagateChangesToRootElements: options?.skipPropagateChangesToRootElements ?? true,
259
+ tryAlignGeolocation: options?.tryAlignGeolocation ?? false,
237
260
  };
238
261
  // check if authorization client is defined
239
262
  if (core_backend_1.IModelHost.authorizationClient === undefined) {
@@ -289,6 +312,20 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
289
312
  this.targetDb.codeValueBehavior = "exact";
290
313
  }
291
314
  /* eslint-enable @itwin/no-internal */
315
+ if (this._options.tryAlignGeolocation) {
316
+ if (this.sourceDb.geographicCoordinateSystem ||
317
+ this.targetDb.geographicCoordinateSystem) {
318
+ core_bentley_1.Logger.logTrace(loggerCategory, "Aligning Additional transforms between imodels due to imodels containing GeographicCoordinateSystem data");
319
+ this._linearSpatialTransform =
320
+ this.calculateTransformFromHelmertTransforms();
321
+ }
322
+ else if (this.sourceDb.ecefLocation && this.targetDb.ecefLocation) {
323
+ core_bentley_1.Logger.logTrace(loggerCategory, "Aligning ECEF Location's between imodels due to imodels not containing GeographicCoordinateSystem data");
324
+ this._linearSpatialTransform = this.calculateEcefTransform();
325
+ }
326
+ else
327
+ core_bentley_1.Logger.logTrace(loggerCategory, "No Geolcation data to align, both GCS and ECEF are undefined");
328
+ }
292
329
  }
293
330
  /** validates that the importer set on the transformer has the same values for its shared options as the transformer.
294
331
  * @note This expects that the importer is already set on the transformer.
@@ -308,7 +345,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
308
345
  /** Dispose any native resources associated with this IModelTransformer. */
309
346
  dispose() {
310
347
  core_bentley_1.Logger.logTrace(loggerCategory, "dispose()");
311
- this.context.dispose();
348
+ this.context[Symbol.dispose]();
312
349
  }
313
350
  /** Log current settings that affect IModelTransformer's behavior. */
314
351
  logSettings() {
@@ -328,14 +365,18 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
328
365
  /** Return the IModelDb where IModelTransformer will store its provenance.
329
366
  * @note This will be [[targetDb]] except when it is a reverse synchronization. In that case it be [[sourceDb]].
330
367
  */
331
- get provenanceDb() {
332
- return this.isReverseSynchronization ? this.sourceDb : this.targetDb;
368
+ async getProvenanceDb() {
369
+ return (await this.getIsReverseSynchronization())
370
+ ? this.sourceDb
371
+ : this.targetDb;
333
372
  }
334
373
  /** Return the IModelDb where IModelTransformer looks for entities referred to by stored provenance.
335
374
  * @note This will be [[sourceDb]] except when it is a reverse synchronization. In that case it be [[targetDb]].
336
375
  */
337
- get provenanceSourceDb() {
338
- return this.isReverseSynchronization ? this.targetDb : this.sourceDb;
376
+ async getProvenanceSourceDb() {
377
+ return (await this.getIsReverseSynchronization())
378
+ ? this.targetDb
379
+ : this.sourceDb;
339
380
  }
340
381
  /** Create an ExternalSourceAspectProps in a standard way for an Element in an iModel --> iModel transformation. */
341
382
  static initElementProvenanceOptions(sourceElementId, targetElementId, args) {
@@ -361,7 +402,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
361
402
  };
362
403
  return aspectProps;
363
404
  }
364
- static initRelationshipProvenanceOptions(sourceRelInstanceId, targetRelInstanceId, args) {
405
+ static async initRelationshipProvenanceOptions(sourceRelInstanceId, targetRelInstanceId, args) {
365
406
  const provenanceDb = args.isReverseSynchronization
366
407
  ? args.sourceDb
367
408
  : args.targetDb;
@@ -371,12 +412,13 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
371
412
  const provenanceRelInstanceId = args.isReverseSynchronization
372
413
  ? sourceRelInstanceId
373
414
  : targetRelInstanceId;
374
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
375
- const elementId = provenanceDb.withPreparedStatement("SELECT SourceECInstanceId FROM bis.ElementRefersToElements WHERE ECInstanceId=?", (stmt) => {
376
- stmt.bindId(1, provenanceRelInstanceId);
377
- nodeAssert(stmt.step() === core_bentley_1.DbResult.BE_SQLITE_ROW);
378
- return stmt.getValue(0).getId();
415
+ const sql = "SELECT SourceECInstanceId FROM bis.ElementRefersToElements WHERE ECInstanceId=?";
416
+ const params = new core_common_1.QueryBinder().bindId(1, provenanceRelInstanceId);
417
+ const reader = provenanceDb.createQueryReader(sql, params, {
418
+ usePrimaryConn: true,
379
419
  });
420
+ nodeAssert(await reader.step(), "relationship provenance query returned no rows");
421
+ const elementId = reader.current[0];
380
422
  const jsonProperties = args.forceOldRelationshipProvenanceMethod
381
423
  ? { targetRelInstanceId }
382
424
  : { provenanceRelInstanceId };
@@ -401,9 +443,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
401
443
  */
402
444
  _forceOldRelationshipProvenanceMethod = false;
403
445
  /** Create an ExternalSourceAspectProps in a standard way for an Element in an iModel --> iModel transformation. */
404
- initElementProvenance(sourceElementId, targetElementId) {
446
+ async initElementProvenance(sourceElementId, targetElementId) {
405
447
  return IModelTransformer.initElementProvenanceOptions(sourceElementId, targetElementId, {
406
- isReverseSynchronization: this.isReverseSynchronization,
448
+ isReverseSynchronization: await this.getIsReverseSynchronization(),
407
449
  targetScopeElementId: this.targetScopeElementId,
408
450
  sourceDb: this.sourceDb,
409
451
  targetDb: this.targetDb,
@@ -414,11 +456,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
414
456
  * The `identifier` property of the ExternalSourceAspect will be the ECInstanceId of the relationship in the master iModel.
415
457
  * The ECInstanceId of the relationship in the branch iModel will be stored in the JsonProperties of the ExternalSourceAspect.
416
458
  */
417
- initRelationshipProvenance(sourceRelationship, targetRelInstanceId) {
459
+ async initRelationshipProvenance(sourceRelationship, targetRelInstanceId) {
418
460
  return IModelTransformer.initRelationshipProvenanceOptions(sourceRelationship.id, targetRelInstanceId, {
419
461
  sourceDb: this.sourceDb,
420
462
  targetDb: this.targetDb,
421
- isReverseSynchronization: this.isReverseSynchronization,
463
+ isReverseSynchronization: await this.getIsReverseSynchronization(),
422
464
  targetScopeElementId: this.targetScopeElementId,
423
465
  forceOldRelationshipProvenanceMethod: this._forceOldRelationshipProvenanceMethod,
424
466
  });
@@ -446,13 +488,13 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
446
488
  * @note empty string and -1 for changeset and index if it was transformed before federation guid update (pre 1.x) and @see [[IModelTransformOptions.branchRelationshipDataBehavior]] === "unsafe-migrate".
447
489
  * @throws if the version is not found in a preexisting scope aspect and @see [[IModelTransformOptions.branchRelationshipDataBehavior]] !== "unsafe-migrate"
448
490
  */
449
- get synchronizationVersion() {
491
+ async getSynchronizationVersion() {
450
492
  if (this._cachedSynchronizationVersion === undefined) {
451
- const provenanceScopeAspect = this.tryGetProvenanceScopeAspect();
493
+ const provenanceScopeAspect = await this.tryGetProvenanceScopeAspect();
452
494
  if (!provenanceScopeAspect) {
453
495
  return { index: -1, id: "" }; // first synchronization.
454
496
  }
455
- const version = this.isReverseSynchronization
497
+ const version = (await this.getIsReverseSynchronization())
456
498
  ? JSON.parse(provenanceScopeAspect.jsonProperties ?? "{}").reverseSyncVersion
457
499
  : provenanceScopeAspect.version;
458
500
  if (!version &&
@@ -474,17 +516,17 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
474
516
  * @returns provenance scope aspect if it exists in the provenanceDb.
475
517
  * Provenance scope aspect is created and inserted into provenanceDb when [[initScopeProvenance]] is invoked.
476
518
  */
477
- tryGetProvenanceScopeAspect() {
478
- const scopeProvenanceAspectProps = IModelTransformer.queryScopeExternalSourceAspect(this.provenanceDb, {
519
+ async tryGetProvenanceScopeAspect() {
520
+ const scopeProvenanceAspectProps = await IModelTransformer.queryScopeExternalSourceAspect(await this.getProvenanceDb(), {
479
521
  id: undefined,
480
522
  classFullName: core_backend_1.ExternalSourceAspect.classFullName,
481
523
  scope: { id: core_common_1.IModel.rootSubjectId },
482
524
  kind: core_backend_1.ExternalSourceAspect.Kind.Scope,
483
525
  element: { id: this.targetScopeElementId ?? core_common_1.IModel.rootSubjectId },
484
- identifier: this.provenanceSourceDb.iModelId,
526
+ identifier: (await this.getProvenanceSourceDb()).iModelId,
485
527
  });
486
528
  return scopeProvenanceAspectProps !== undefined
487
- ? this.provenanceDb.elements.getAspect(scopeProvenanceAspectProps.aspectId)
529
+ ? (await this.getProvenanceDb()).elements.getAspect(scopeProvenanceAspectProps.aspectId)
488
530
  : undefined;
489
531
  }
490
532
  /**
@@ -493,7 +535,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
493
535
  * @returns the last synced version (changesetId) on the target scope's external source aspect,
494
536
  * if this was a [BriefcaseDb]($backend)
495
537
  */
496
- initScopeProvenance() {
538
+ async initScopeProvenance() {
539
+ const provenanceDb = await this.getProvenanceDb();
540
+ const sourceProvenanceDb = await this.getProvenanceSourceDb();
497
541
  const aspectProps = {
498
542
  id: undefined,
499
543
  version: undefined,
@@ -503,11 +547,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
503
547
  relClassName: core_backend_1.ElementOwnsExternalSourceAspects.classFullName,
504
548
  },
505
549
  scope: { id: core_common_1.IModel.rootSubjectId }, // the root Subject scopes scope elements
506
- identifier: this.provenanceSourceDb.iModelId,
550
+ identifier: sourceProvenanceDb.iModelId,
507
551
  kind: core_backend_1.ExternalSourceAspect.Kind.Scope,
508
552
  jsonProperties: undefined,
509
553
  };
510
- const foundEsaProps = IModelTransformer.queryScopeExternalSourceAspect(this.provenanceDb, aspectProps); // this query includes "identifier"
554
+ const foundEsaProps = await IModelTransformer.queryScopeExternalSourceAspect(provenanceDb, aspectProps); // this query includes "identifier"
511
555
  if (foundEsaProps === undefined) {
512
556
  aspectProps.version = ""; // empty since never before transformed. Will be updated in [[finalizeTransformation]]
513
557
  aspectProps.jsonProperties = {
@@ -524,20 +568,19 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
524
568
  AND Kind=:kind
525
569
  LIMIT 1
526
570
  `;
527
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
528
- const hasConflictingScope = this.provenanceDb.withPreparedStatement(sql,
529
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
530
- (statement) => {
531
- statement.bindId("elementId", aspectProps.element.id);
532
- statement.bindId("scopeId", aspectProps.scope.id); // this scope.id can never be invalid, we create it above
533
- statement.bindString("kind", aspectProps.kind);
534
- return core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step();
571
+ const params = new core_common_1.QueryBinder();
572
+ params.bindId("elementId", aspectProps.element.id);
573
+ params.bindId("scopeId", aspectProps.scope.id); // this scope.id can never be invalid, we create it above
574
+ params.bindString("kind", aspectProps.kind);
575
+ const reader = provenanceDb.createQueryReader(sql, params, {
576
+ usePrimaryConn: true,
535
577
  });
578
+ const hasConflictingScope = await reader.step();
536
579
  if (hasConflictingScope) {
537
580
  throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, "Provenance scope conflict");
538
581
  }
539
582
  if (!this._options.noProvenance) {
540
- const id = this.provenanceDb.elements.insertAspect({
583
+ const id = provenanceDb.elements.insertAspect({
541
584
  ...aspectProps,
542
585
  jsonProperties: JSON.stringify(aspectProps.jsonProperties),
543
586
  });
@@ -557,7 +600,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
557
600
  const oldProps = JSON.parse(JSON.stringify(aspectProps));
558
601
  if (this.handleUnsafeMigrate(aspectProps)) {
559
602
  core_bentley_1.Logger.logInfo(loggerCategory, "Unsafe migrate made a change to the target scope's external source aspect. Updating aspect in database.", { oldProps, newProps: aspectProps });
560
- this.provenanceDb.elements.updateAspect({
603
+ provenanceDb.elements.updateAspect({
561
604
  ...aspectProps,
562
605
  jsonProperties: JSON.stringify(aspectProps.jsonProperties),
563
606
  });
@@ -624,7 +667,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
624
667
  * @note provenance is done by federation guids where possible
625
668
  * @note this may execute on each element more than once! Only use in cases where that is handled
626
669
  */
627
- static forEachTrackedElement(args) {
670
+ static async forEachTrackedElement(args) {
628
671
  if (args.provenanceDb === args.provenanceSourceDb)
629
672
  return;
630
673
  if (!args.provenanceDb.containsClass(core_backend_1.ExternalSourceAspect.classFullName)) {
@@ -649,42 +692,28 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
649
692
  // NOTE: if we exposed the native attach database support,
650
693
  // we could get the intersection of fed guids in one query, not sure if it would be faster
651
694
  // OR we could do a raw sqlite query...
652
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
653
- sourceDb.withStatement(elementIdByFedGuidQuery, (sourceStmt) =>
654
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
655
- targetDb.withStatement(elementIdByFedGuidQuery, (targetStmt) => {
656
- if (sourceStmt.step() !== core_bentley_1.DbResult.BE_SQLITE_ROW)
657
- return;
658
- let sourceRow = sourceStmt.getRow();
659
- if (targetStmt.step() !== core_bentley_1.DbResult.BE_SQLITE_ROW)
660
- return;
661
- let targetRow = targetStmt.getRow();
662
- // NOTE: these comparisons rely upon the lowercase of the guid,
663
- // and the fact that '0' < '9' < a' < 'f' in ascii/utf8
664
- while (true) {
665
- const currSourceRow = sourceRow, currTargetRow = targetRow;
666
- if (currSourceRow.federationGuid !== undefined &&
667
- currTargetRow.federationGuid !== undefined &&
668
- currSourceRow.federationGuid === currTargetRow.federationGuid) {
669
- // data flow direction is always sourceDb -> targetDb and it does not depend on where the explicit element provenance is stored
670
- args.fn(sourceRow.id, targetRow.id);
671
- }
672
- if (currTargetRow.federationGuid === undefined ||
673
- (currSourceRow.federationGuid !== undefined &&
674
- currSourceRow.federationGuid >= currTargetRow.federationGuid)) {
675
- if (targetStmt.step() !== core_bentley_1.DbResult.BE_SQLITE_ROW)
676
- return;
677
- targetRow = targetStmt.getRow();
678
- }
679
- if (currSourceRow.federationGuid === undefined ||
680
- (currTargetRow.federationGuid !== undefined &&
681
- currSourceRow.federationGuid <= currTargetRow.federationGuid)) {
682
- if (sourceStmt.step() !== core_bentley_1.DbResult.BE_SQLITE_ROW)
683
- return;
684
- sourceRow = sourceStmt.getRow();
685
- }
695
+ const sourceReader = sourceDb.createQueryReader(elementIdByFedGuidQuery, undefined, { usePrimaryConn: true });
696
+ const targetReader = targetDb.createQueryReader(elementIdByFedGuidQuery, undefined, { usePrimaryConn: true });
697
+ let hasSourceRow = await sourceReader.step();
698
+ let hasTargetRow = await targetReader.step();
699
+ while (hasSourceRow && hasTargetRow) {
700
+ const sourceFedGuid = sourceReader.current.federationGuid;
701
+ const targetFedGuid = targetReader.current.federationGuid;
702
+ if (sourceFedGuid !== undefined &&
703
+ targetFedGuid !== undefined &&
704
+ sourceFedGuid === targetFedGuid) {
705
+ // data flow direction is always sourceDb -> targetDb and it does not depend on where the explicit element provenance is stored
706
+ args.fn(sourceReader.current.id, targetReader.current.id);
686
707
  }
687
- }));
708
+ if (targetFedGuid === undefined ||
709
+ (sourceFedGuid !== undefined && sourceFedGuid >= targetFedGuid)) {
710
+ hasTargetRow = await targetReader.step();
711
+ }
712
+ if (sourceFedGuid === undefined ||
713
+ (targetFedGuid !== undefined && sourceFedGuid <= targetFedGuid)) {
714
+ hasSourceRow = await sourceReader.step();
715
+ }
716
+ }
688
717
  // query for provenanceDb
689
718
  const provenanceAspectsQuery = `
690
719
  SELECT esa.Identifier, Element.Id
@@ -695,27 +724,26 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
695
724
  // Technically this will a second time call the function (as documented) on
696
725
  // victims of the old provenance method that have both fedguids and an inserted aspect.
697
726
  // But this is a private function with one known caller where that doesn't matter
698
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
699
- args.provenanceDb.withPreparedStatement(provenanceAspectsQuery, (stmt) => {
700
- const runFnInDataFlowDirection = (sourceId, targetId) => args.isReverseSynchronization
701
- ? args.fn(sourceId, targetId)
702
- : args.fn(targetId, sourceId);
703
- stmt.bindId("scopeId", args.targetScopeElementId);
704
- stmt.bindString("kind", core_backend_1.ExternalSourceAspect.Kind.Element);
705
- while (core_bentley_1.DbResult.BE_SQLITE_ROW === stmt.step()) {
706
- // ExternalSourceAspect.Identifier is of type string
707
- const aspectIdentifier = stmt.getValue(0).getString();
708
- const elementId = stmt.getValue(1).getId();
709
- runFnInDataFlowDirection(elementId, aspectIdentifier);
710
- }
711
- });
712
- }
713
- forEachTrackedElement(fn) {
727
+ const runFnInDataFlowDirection = (sourceId, targetId) => args.isReverseSynchronization
728
+ ? args.fn(sourceId, targetId)
729
+ : args.fn(targetId, sourceId);
730
+ const params = new core_common_1.QueryBinder();
731
+ params.bindId("scopeId", args.targetScopeElementId);
732
+ params.bindString("kind", core_backend_1.ExternalSourceAspect.Kind.Element);
733
+ const provenanceReader = args.provenanceDb.createQueryReader(provenanceAspectsQuery, params, { usePrimaryConn: true });
734
+ for await (const row of provenanceReader) {
735
+ // ExternalSourceAspect.Identifier is of type string
736
+ const aspectIdentifier = row[0];
737
+ const elementId = row.id;
738
+ runFnInDataFlowDirection(elementId, aspectIdentifier);
739
+ }
740
+ }
741
+ async forEachTrackedElement(fn) {
714
742
  return IModelTransformer.forEachTrackedElement({
715
- provenanceSourceDb: this.provenanceSourceDb,
716
- provenanceDb: this.provenanceDb,
743
+ provenanceSourceDb: await this.getProvenanceSourceDb(),
744
+ provenanceDb: await this.getProvenanceDb(),
717
745
  targetScopeElementId: this.targetScopeElementId,
718
- isReverseSynchronization: this.isReverseSynchronization,
746
+ isReverseSynchronization: await this.getIsReverseSynchronization(),
719
747
  fn,
720
748
  skipPropagateChangesToRootElements: this._options.skipPropagateChangesToRootElements ?? true,
721
749
  });
@@ -727,23 +755,26 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
727
755
  * @param entityInProvenanceSourceId
728
756
  * @returns the elementId that the ESA is stored on, esa.Element.Id
729
757
  */
730
- _queryProvenanceForElement(entityInProvenanceSourceId) {
731
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
732
- return this.provenanceDb.withPreparedStatement(`
758
+ async _queryProvenanceForElement(entityInProvenanceSourceId) {
759
+ const sql = `
733
760
  SELECT esa.Element.Id
734
761
  FROM Bis.ExternalSourceAspect esa
735
762
  WHERE esa.Kind=?
736
763
  AND esa.Scope.Id=?
737
764
  AND esa.Identifier=?
738
- `, (stmt) => {
739
- stmt.bindString(1, core_backend_1.ExternalSourceAspect.Kind.Element);
740
- stmt.bindId(2, this.targetScopeElementId);
741
- stmt.bindString(3, entityInProvenanceSourceId);
742
- if (stmt.step() === core_bentley_1.DbResult.BE_SQLITE_ROW)
743
- return stmt.getValue(0).getId();
744
- else
745
- return undefined;
765
+ `;
766
+ const params = new core_common_1.QueryBinder();
767
+ params.bindString(1, core_backend_1.ExternalSourceAspect.Kind.Element);
768
+ params.bindId(2, this.targetScopeElementId);
769
+ params.bindString(3, entityInProvenanceSourceId);
770
+ const result = (await this.getProvenanceDb()).createQueryReader(sql, params, {
771
+ usePrimaryConn: true,
746
772
  });
773
+ if (await result.step()) {
774
+ return result.current.id;
775
+ }
776
+ else
777
+ return undefined;
747
778
  }
748
779
  /**
749
780
  * Queries the provenanceDb for an ESA whose identifier is equal to the provided 'entityInProvenanceSourceId'.
@@ -752,35 +783,38 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
752
783
  * @param entityInProvenanceSourceId
753
784
  * @returns
754
785
  */
755
- _queryProvenanceForRelationship(entityInProvenanceSourceId, sourceRelInfo) {
756
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
757
- return this.provenanceDb.withPreparedStatement(`
758
- SELECT
759
- ECInstanceId,
760
- JSON_EXTRACT(JsonProperties, '$.targetRelInstanceId'),
761
- JSON_EXTRACT(JsonProperties, '$.provenanceRelInstanceId')
762
- FROM Bis.ExternalSourceAspect
763
- WHERE Kind=?
764
- AND Scope.Id=?
765
- AND Identifier=?
766
- `, (stmt) => {
767
- stmt.bindString(1, core_backend_1.ExternalSourceAspect.Kind.Relationship);
768
- stmt.bindId(2, this.targetScopeElementId);
769
- stmt.bindString(3, entityInProvenanceSourceId);
770
- if (stmt.step() !== core_bentley_1.DbResult.BE_SQLITE_ROW)
771
- return undefined;
772
- const aspectId = stmt.getValue(0).getId();
773
- const provenanceRelInstIdVal = stmt.getValue(2);
774
- const provenanceRelInstanceId = !provenanceRelInstIdVal.isNull
775
- ? provenanceRelInstIdVal.getString()
776
- : this._queryTargetRelId(sourceRelInfo);
786
+ async _queryProvenanceForRelationship(entityInProvenanceSourceId, sourceRelInfo) {
787
+ const sql = `
788
+ SELECT
789
+ ECInstanceId,
790
+ JSON_EXTRACT(JsonProperties, '$.provenanceRelInstanceId') AS provenanceRelInstId
791
+ FROM Bis.ExternalSourceAspect
792
+ WHERE Kind=?
793
+ AND Scope.Id=?
794
+ AND Identifier=?
795
+ `;
796
+ const params = new core_common_1.QueryBinder();
797
+ params.bindString(1, core_backend_1.ExternalSourceAspect.Kind.Relationship);
798
+ params.bindId(2, this.targetScopeElementId);
799
+ params.bindString(3, entityInProvenanceSourceId);
800
+ const result = (await this.getProvenanceDb()).createQueryReader(sql, params, {
801
+ usePrimaryConn: true,
802
+ });
803
+ if (await result.step()) {
804
+ const aspectId = result.current.id;
805
+ const provenanceRelInstId = result.current.provenanceRelInstId;
806
+ const provenanceRelInstanceId = provenanceRelInstId !== undefined
807
+ ? provenanceRelInstId
808
+ : await this._queryTargetRelId(sourceRelInfo);
777
809
  return {
778
810
  aspectId,
779
811
  relationshipId: provenanceRelInstanceId,
780
812
  };
781
- });
813
+ }
814
+ else
815
+ return undefined;
782
816
  }
783
- _queryTargetRelId(sourceRelInfo) {
817
+ async _queryTargetRelId(sourceRelInfo) {
784
818
  const targetRelInfo = {
785
819
  sourceId: this.context.findTargetElementId(sourceRelInfo.sourceId),
786
820
  targetId: this.context.findTargetElementId(sourceRelInfo.targetId),
@@ -788,60 +822,53 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
788
822
  if (targetRelInfo.sourceId === undefined ||
789
823
  targetRelInfo.targetId === undefined)
790
824
  return undefined; // couldn't find an element, rel is invalid or deleted
791
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
792
- return this.targetDb.withPreparedStatement(`
793
- SELECT ECInstanceId
794
- FROM bis.ElementRefersToElements
795
- WHERE SourceECInstanceId=?
796
- AND TargetECInstanceId=?
797
- AND ECClassId=?
798
- `, (stmt) => {
799
- stmt.bindId(1, targetRelInfo.sourceId);
800
- stmt.bindId(2, targetRelInfo.targetId);
801
- stmt.bindId(3, this._targetClassNameToClassId(sourceRelInfo.classFullName));
802
- if (stmt.step() !== core_bentley_1.DbResult.BE_SQLITE_ROW)
803
- return undefined;
804
- return stmt.getValue(0).getId();
825
+ const sql = `
826
+ select ecinstanceid
827
+ from bis.elementreferstoelements
828
+ where sourceecinstanceid=?
829
+ and targetecinstanceid=?
830
+ and ecclassid=?
831
+ `;
832
+ const params = new core_common_1.QueryBinder();
833
+ params.bindId(1, targetRelInfo.sourceId);
834
+ params.bindId(2, targetRelInfo.targetId);
835
+ params.bindId(3, await this._targetClassNameToClassId(sourceRelInfo.classFullName));
836
+ const result = this.targetDb.createQueryReader(sql, params, {
837
+ usePrimaryConn: true,
805
838
  });
839
+ if (await result.step())
840
+ return result.current.id;
841
+ else
842
+ return undefined;
806
843
  }
807
844
  _targetClassNameToClassIdCache = new Map();
808
- _targetClassNameToClassId(classFullName) {
845
+ async _targetClassNameToClassId(classFullName) {
809
846
  let classId = this._targetClassNameToClassIdCache.get(classFullName);
810
847
  if (classId === undefined) {
811
- classId = this._getRelClassId(this.targetDb, classFullName);
848
+ classId = await this._getRelClassId(this.targetDb, classFullName);
812
849
  this._targetClassNameToClassIdCache.set(classFullName, classId);
813
850
  }
814
851
  return classId;
815
852
  }
816
853
  // NOTE: this doesn't handle remapped element classes,
817
854
  // but is only used for relationships rn
818
- _getRelClassId(db, classFullName) {
819
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
820
- return db.withPreparedStatement(`
855
+ async _getRelClassId(db, classFullName) {
856
+ const sql = `
821
857
  SELECT c.ECInstanceId
822
858
  FROM ECDbMeta.ECClassDef c
823
859
  JOIN ECDbMeta.ECSchemaDef s ON c.Schema.Id=s.ECInstanceId
824
860
  WHERE s.Name=? AND c.Name=?
825
- `, (stmt) => {
826
- const [schemaName, className] = classFullName.indexOf(".") !== -1
827
- ? classFullName.split(".")
828
- : classFullName.split(":");
829
- stmt.bindString(1, schemaName);
830
- stmt.bindString(2, className);
831
- if (stmt.step() === core_bentley_1.DbResult.BE_SQLITE_ROW)
832
- return stmt.getValue(0).getId();
833
- (0, core_bentley_1.assert)(false, "relationship was not found");
834
- });
835
- }
836
- _queryElemIdByFedGuid(db, fedGuid) {
837
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
838
- return db.withPreparedStatement("SELECT ECInstanceId FROM Bis.Element WHERE FederationGuid=?", (stmt) => {
839
- stmt.bindGuid(1, fedGuid);
840
- if (stmt.step() === core_bentley_1.DbResult.BE_SQLITE_ROW)
841
- return stmt.getValue(0).getId();
842
- else
843
- return undefined;
844
- });
861
+ `;
862
+ const params = new core_common_1.QueryBinder();
863
+ const [schemaName, className] = classFullName.indexOf(".") !== -1
864
+ ? classFullName.split(".")
865
+ : classFullName.split(":");
866
+ params.bindString(1, schemaName);
867
+ params.bindString(2, className);
868
+ const result = db.createQueryReader(sql, params, { usePrimaryConn: true });
869
+ if (await result.step())
870
+ return result.current.id;
871
+ (0, core_bentley_1.assert)(false, "relationship was not found");
845
872
  }
846
873
  /** Returns `true` if *brute force* delete detections should be run.
847
874
  * @note This is only called if [[IModelTransformOptions.forceExternalSourceAspectProvenance]] option is true
@@ -851,53 +878,15 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
851
878
  nodeAssert(this._syncType !== undefined);
852
879
  return this._syncType === "not-sync";
853
880
  }
854
- /**
855
- * Detect Element deletes using ExternalSourceAspects in the target iModel and a *brute force* comparison against Elements
856
- * in the source iModel.
857
- * @deprecated in 1.x. Do not use this. // FIXME<MIKE>: how to better explain this?
858
- * This method is only called during [[process]] when [[IModelTransformOptions.argsForProcessChanges]] is undefined and the option
859
- * [[IModelTransformOptions.forceExternalSourceAspectProvenance]] is enabled. It is not
860
- * necessary when calling [[process]] with [[IModelTransformOptions.argsForProcessChanges]] defined, since changeset information is sufficient.
861
- * @note you do not need to call this directly unless processing a subset of an iModel.
862
- * @throws [[IModelError]] If the required provenance information is not available to detect deletes.
863
- */
864
- async detectElementDeletes() {
865
- const sql = `
866
- SELECT Identifier, Element.Id
867
- FROM BisCore.ExternalSourceAspect
868
- WHERE Scope.Id=:scopeId
869
- AND Kind=:kind
870
- `;
871
- nodeAssert(!this.isReverseSynchronization, "synchronizations with processChanges already detect element deletes, don't call detectElementDeletes");
872
- // Reported issue: https://github.com/iTwin/itwinjs-core/issues/7989
873
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
874
- this.provenanceDb.withPreparedStatement(sql, (stmt) => {
875
- stmt.bindId("scopeId", this.targetScopeElementId);
876
- stmt.bindString("kind", core_backend_1.ExternalSourceAspect.Kind.Element);
877
- while (core_bentley_1.DbResult.BE_SQLITE_ROW === stmt.step()) {
878
- // ExternalSourceAspect.Identifier is of type string
879
- const aspectIdentifier = stmt.getValue(0).getString();
880
- if (!core_bentley_1.Id64.isValidId64(aspectIdentifier)) {
881
- continue;
882
- }
883
- const targetElemId = stmt.getValue(1).getId();
884
- const wasDeletedInSource = !EntityUnifier_1.EntityUnifier.exists(this.sourceDb, {
885
- entityReference: `e${aspectIdentifier}`,
886
- });
887
- if (wasDeletedInSource)
888
- this.importer.deleteElement(targetElemId);
889
- }
890
- });
891
- }
892
881
  /** Transform the specified sourceElement into ElementProps for the target iModel.
893
882
  * @param sourceElement The Element from the source iModel to transform.
894
883
  * @returns ElementProps for the target iModel.
895
884
  * @note A subclass can override this method to provide custom transform behavior.
896
885
  * @note This can be called more than once for an element in arbitrary order, so it should not have side-effects.
897
886
  */
898
- onTransformElement(sourceElement) {
887
+ async onTransformElement(sourceElement) {
899
888
  core_bentley_1.Logger.logTrace(loggerCategory, `onTransformElement(${sourceElement.id}) "${sourceElement.getDisplayLabel()}"`);
900
- const targetElementProps = this.context.cloneElement(sourceElement, { binaryGeometry: this._options.cloneUsingBinaryGeometry });
889
+ const targetElementProps = await this.context.cloneElement(sourceElement, { binaryGeometry: this._options.cloneUsingBinaryGeometry });
901
890
  // Special case: source element is the root subject
902
891
  if (sourceElement.id === core_common_1.IModel.rootSubjectId) {
903
892
  const targetElementId = this.context.findTargetElementId(sourceElement.id);
@@ -918,8 +907,87 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
918
907
  targetElementProps.jsonProperties.Subject.Job = undefined;
919
908
  }
920
909
  }
910
+ if (this._linearSpatialTransform !== undefined &&
911
+ sourceElement instanceof core_backend_1.GeometricElement3d) {
912
+ // can check the sourceElement since this IModelTransformer does not remap classes
913
+ const placement = core_common_1.Placement3d.fromJSON(targetElementProps.placement);
914
+ if (placement.isValid) {
915
+ placement.multiplyTransform(this._linearSpatialTransform);
916
+ targetElementProps.placement = placement;
917
+ }
918
+ }
921
919
  return targetElementProps;
922
920
  }
921
+ /**
922
+ * Calculate the transform between two ECEF locations
923
+ * @param srcDb
924
+ * @param targetDb
925
+ * @returns Transform that converts relative coordinates in the source iModel to relative coordinates in the target iModel.
926
+ * @note This can only be used if both source and target iModels are linearly located
927
+ */
928
+ calculateEcefTransform() {
929
+ const srcEcefLoc = this.sourceDb.ecefLocation;
930
+ const targetEcefLoc = this.targetDb.ecefLocation;
931
+ if (srcEcefLoc === undefined || targetEcefLoc === undefined) {
932
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NoGeoLocation, "Both source and target ECEF locations must be defined to calculate the transform.");
933
+ }
934
+ if (srcEcefLoc.getTransform().isAlmostEqual(targetEcefLoc.getTransform())) {
935
+ core_bentley_1.Logger.logTrace(loggerCategory, "ECEF data is already aligned. No spatial transforms needed.");
936
+ return undefined;
937
+ }
938
+ const srcSpatialToECEF = srcEcefLoc.getTransform(); // converts relative to ECEF in relation to source
939
+ const targetECEFToSpatial = targetEcefLoc.getTransform().inverse(); // converts ECEF to relative in relation to target
940
+ if (!targetECEFToSpatial) {
941
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NoGeoLocation, "Failed to invert target ECEF transform.");
942
+ }
943
+ const ecefTransform = targetECEFToSpatial.multiplyTransformTransform(srcSpatialToECEF); // chain both transforms
944
+ return ecefTransform;
945
+ }
946
+ static convertHelmertToTransform(helmert) {
947
+ if (!helmert) {
948
+ return core_geometry_1.Transform.createIdentity();
949
+ }
950
+ const rotationXY = core_geometry_1.Matrix3d.createRotationAroundAxisIndex(core_geometry_1.AxisIndex.Z, core_geometry_1.Angle.createDegrees(helmert?.rotDeg));
951
+ rotationXY.scaleColumnsInPlace(helmert.scale, helmert.scale, 1.0);
952
+ const translation = core_geometry_1.Vector3d.create(helmert.translationX, helmert.translationY, helmert.translationZ);
953
+ const helmertTransform = core_geometry_1.Transform.createRefs(translation, rotationXY);
954
+ return helmertTransform;
955
+ }
956
+ calculateTransformFromHelmertTransforms() {
957
+ if (this.sourceDb.geographicCoordinateSystem?.horizontalCRS === undefined ||
958
+ this.sourceDb.geographicCoordinateSystem?.verticalCRS === undefined) {
959
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "Source iModel does not have a geographic coordinate system defined.");
960
+ }
961
+ if (this.targetDb.geographicCoordinateSystem?.horizontalCRS === undefined ||
962
+ this.targetDb.geographicCoordinateSystem.verticalCRS === undefined) {
963
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "Target iModel does not have a geographic coordinate system defined.");
964
+ }
965
+ if (!this.sourceDb.geographicCoordinateSystem.horizontalCRS.equals(this.targetDb.geographicCoordinateSystem.horizontalCRS) ||
966
+ !this.sourceDb.geographicCoordinateSystem.verticalCRS.equals(this.targetDb.geographicCoordinateSystem.verticalCRS)) {
967
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.MismatchGcs, "Source and target geographic coordinate systems must match to calculate the spatial transform.");
968
+ }
969
+ if (this.sourceDb.geographicCoordinateSystem.additionalTransform ===
970
+ this.targetDb.geographicCoordinateSystem.additionalTransform) {
971
+ core_bentley_1.Logger.logTrace(loggerCategory, "Geolocation data is already aligned. No spatial transforms needed.");
972
+ return undefined;
973
+ }
974
+ const srcScale = this.sourceDb.geographicCoordinateSystem.additionalTransform
975
+ ?.helmert2DWithZOffset?.scale ?? 1;
976
+ const targetScale = this.targetDb.geographicCoordinateSystem.additionalTransform
977
+ ?.helmert2DWithZOffset?.scale ?? 1;
978
+ if (srcScale !== targetScale) {
979
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.MismatchGcs, "Spatial transform is non rigid. Source and target Helmert transforms must have the same scale to calculate a rigid spatial transform.");
980
+ }
981
+ const srcTransform = IModelTransformer.convertHelmertToTransform(this.sourceDb.geographicCoordinateSystem.additionalTransform
982
+ ?.helmert2DWithZOffset); // moves elements to where src helmert transform would move them at render time
983
+ const targetTransformInv = IModelTransformer.convertHelmertToTransform(this.targetDb.geographicCoordinateSystem.additionalTransform
984
+ ?.helmert2DWithZOffset).inverse(); // negates target helmert transform that is applied at render time
985
+ if (!targetTransformInv) {
986
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NoGeoLocation, "Failed to invert target Helmert transform.");
987
+ }
988
+ const combinedTransform = targetTransformInv.multiplyTransformTransform(srcTransform);
989
+ return combinedTransform;
990
+ }
923
991
  // if undefined, it can be initialized by calling [[this.processChangesets]]
924
992
  _deletedSourceRelationshipData = undefined;
925
993
  /** Returns true if a change within sourceElement is detected.
@@ -932,7 +1000,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
932
1000
  sourceDbChanges.element.insertIds.has(sourceElement.id) ||
933
1001
  sourceDbChanges.element.updateIds.has(sourceElement.id));
934
1002
  }
935
- completePartiallyCommittedElements() {
1003
+ async completePartiallyCommittedElements() {
936
1004
  for (const sourceElementId of this._partiallyCommittedElementIds) {
937
1005
  const sourceElement = this.sourceDb.elements.getElement({
938
1006
  id: sourceElementId,
@@ -943,25 +1011,25 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
943
1011
  if (core_bentley_1.Id64.isInvalid(targetId)) {
944
1012
  throw new Error(`source-target element mapping not found for element "${sourceElementId}" when completing partially committed elements. This is a bug.`);
945
1013
  }
946
- const targetProps = this.onTransformElement(sourceElement);
1014
+ const targetProps = await this.onTransformElement(sourceElement);
947
1015
  this.targetDb.elements.updateElement({ ...targetProps, id: targetId });
948
1016
  }
949
1017
  }
950
- completePartiallyCommittedAspects() {
1018
+ async completePartiallyCommittedAspects() {
951
1019
  for (const sourceAspectId of this._partiallyCommittedAspectIds) {
952
1020
  const sourceAspect = this.sourceDb.elements.getAspect(sourceAspectId);
953
1021
  const targetAspectId = this.context.findTargetAspectId(sourceAspectId);
954
1022
  if (core_bentley_1.Id64.isInvalid(targetAspectId)) {
955
1023
  throw new Error(`source-target aspect mapping not found for aspect "${sourceAspectId}" when completing partially committed aspects. This is a bug.`);
956
1024
  }
957
- const targetAspectProps = this.onTransformElementAspect(sourceAspect);
1025
+ const targetAspectProps = await this.onTransformElementAspect(sourceAspect);
958
1026
  this.targetDb.elements.updateAspect({
959
1027
  ...targetAspectProps,
960
1028
  id: targetAspectId,
961
1029
  });
962
1030
  }
963
1031
  }
964
- doAllReferencesExistInTarget(entity) {
1032
+ async doAllReferencesExistInTarget(entity) {
965
1033
  let allReferencesExist = true;
966
1034
  for (const referenceId of entity.getReferenceIds()) {
967
1035
  const referencedEntityId = core_backend_1.EntityReferences.toId64(referenceId);
@@ -971,7 +1039,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
971
1039
  continue;
972
1040
  }
973
1041
  if (allReferencesExist &&
974
- !core_backend_1.EntityReferences.isValid(this.context.findTargetEntityId(referenceId))) {
1042
+ !core_backend_1.EntityReferences.isValid(await this.context.findTargetEntityId(referenceId))) {
975
1043
  // if we care about references existing then we cannot return early and must check all other references.
976
1044
  if (this._options.danglingReferencesBehavior === "ignore") {
977
1045
  return false;
@@ -979,13 +1047,13 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
979
1047
  allReferencesExist = false;
980
1048
  }
981
1049
  if (this._options.danglingReferencesBehavior === "reject") {
982
- this.assertReferenceExistsInSource(referenceId, entity);
1050
+ await this.assertReferenceExistsInSource(referenceId, entity);
983
1051
  }
984
1052
  }
985
1053
  return allReferencesExist;
986
1054
  }
987
- assertReferenceExistsInSource(referenceId, entity) {
988
- const referencedExistsInSource = EntityUnifier_1.EntityUnifier.exists(this.sourceDb, {
1055
+ async assertReferenceExistsInSource(referenceId, entity) {
1056
+ const referencedExistsInSource = await EntityUnifier_1.EntityUnifier.exists(this.sourceDb, {
989
1057
  entityReference: referenceId,
990
1058
  });
991
1059
  if (!referencedExistsInSource) {
@@ -1027,6 +1095,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1027
1095
  * @internal do not call, override or implement this, it will be removed
1028
1096
  */
1029
1097
  async preExportElement(sourceElement) {
1098
+ if (!this.hasElementChanged(sourceElement)) {
1099
+ core_bentley_1.Logger.logTrace(loggerCategory, `Skipping unchanged element (${sourceElement.id}, ${sourceElement.getDisplayLabel()}).`);
1100
+ return;
1101
+ }
1030
1102
  const elemClass = sourceElement.constructor;
1031
1103
  const unresolvedReferences = elemClass.requiredReferenceKeys
1032
1104
  .map((referenceKey) => {
@@ -1061,7 +1133,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1061
1133
  .flat();
1062
1134
  if (unresolvedReferences.length > 0) {
1063
1135
  for (const reference of unresolvedReferences) {
1064
- const processState = this.getElemTransformState(reference);
1136
+ const processState = await this.getElemTransformState(reference);
1065
1137
  // must export element first
1066
1138
  if (processState.needsElemImport)
1067
1139
  await this.exporter.exportElement(reference);
@@ -1070,23 +1142,23 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1070
1142
  }
1071
1143
  }
1072
1144
  }
1073
- getElemTransformState(elementId) {
1074
- const dbHasModel = (db, id) => {
1145
+ async getElemTransformState(elementId) {
1146
+ const dbHasModel = async (db, id) => {
1075
1147
  const maybeModelId = core_backend_1.EntityReferences.fromEntityType(id, core_common_1.ConcreteEntityTypes.Model);
1076
1148
  return EntityUnifier_1.EntityUnifier.exists(db, { entityReference: maybeModelId });
1077
1149
  };
1078
- const isSubModeled = dbHasModel(this.sourceDb, elementId);
1150
+ const isSubModeled = await dbHasModel(this.sourceDb, elementId);
1079
1151
  const idOfElemInTarget = this.context.findTargetElementId(elementId);
1080
1152
  const isElemInTarget = core_bentley_1.Id64.invalid !== idOfElemInTarget;
1081
1153
  const needsModelImport = isSubModeled &&
1082
- (!isElemInTarget || !dbHasModel(this.targetDb, idOfElemInTarget));
1154
+ (!isElemInTarget || !(await dbHasModel(this.targetDb, idOfElemInTarget)));
1083
1155
  return { needsElemImport: !isElemInTarget, needsModelImport };
1084
1156
  }
1085
1157
  /** Override of [IModelExportHandler.onExportElement]($transformer) that imports an element into the target iModel when it is exported from the source iModel.
1086
1158
  * This override calls [[onTransformElement]] and then [IModelImporter.importElement]($transformer) to update the target iModel.
1087
1159
  */
1088
- onExportElement(sourceElement) {
1089
- let targetElementId;
1160
+ async onExportElement(sourceElement) {
1161
+ let targetElementId = core_bentley_1.Id64.invalid;
1090
1162
  let targetElementProps;
1091
1163
  if (this._options.wasSourceIModelCopiedToTarget) {
1092
1164
  targetElementId = sourceElement.id;
@@ -1095,14 +1167,14 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1095
1167
  }
1096
1168
  else {
1097
1169
  targetElementId = this.context.findTargetElementId(sourceElement.id);
1098
- targetElementProps = this.onTransformElement(sourceElement);
1170
+ targetElementProps = await this.onTransformElement(sourceElement);
1099
1171
  }
1100
1172
  // if an existing remapping was not yet found, check by FederationGuid
1101
1173
  if (this.context.isBetweenIModels &&
1102
1174
  !core_bentley_1.Id64.isValid(targetElementId) &&
1103
1175
  sourceElement.federationGuid !== undefined) {
1104
1176
  targetElementId =
1105
- this._queryElemIdByFedGuid(this.targetDb, sourceElement.federationGuid) ?? core_bentley_1.Id64.invalid;
1177
+ this.targetDb.elements.getIdFromFederationGuid(sourceElement.federationGuid) ?? core_bentley_1.Id64.invalid;
1106
1178
  if (core_bentley_1.Id64.isValid(targetElementId))
1107
1179
  this.context.remapElement(sourceElement.id, targetElementId); // record that the targetElement was found
1108
1180
  }
@@ -1112,7 +1184,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1112
1184
  // respond the same way to undefined code value as the @see Code class, but don't use that class because it trims
1113
1185
  // whitespace from the value, and there are iModels out there with untrimmed whitespace that we ought not to trim
1114
1186
  targetElementProps.code.value = targetElementProps.code.value ?? "";
1115
- const maybeTargetElementId = this.targetDb.elements.queryElementIdByCode(targetElementProps.code);
1187
+ const maybeTargetElementId = await this.queryElementIdByCode(this.targetDb, targetElementProps.code);
1116
1188
  if (undefined !== maybeTargetElementId) {
1117
1189
  const maybeTargetElem = this.targetDb.elements.getElement(maybeTargetElementId);
1118
1190
  if (maybeTargetElem.classFullName === targetElementProps.classFullName) {
@@ -1129,7 +1201,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1129
1201
  core_bentley_1.Logger.logTrace(loggerCategory, `Skipping unchanged element (${sourceElement.id}, ${sourceElement.getDisplayLabel()}).`);
1130
1202
  return;
1131
1203
  }
1132
- if (!this.doAllReferencesExistInTarget(sourceElement)) {
1204
+ if (!(await this.doAllReferencesExistInTarget(sourceElement))) {
1133
1205
  this._partiallyCommittedElementIds.add(sourceElement.id);
1134
1206
  }
1135
1207
  // targetElementId will be valid (indicating update) or undefined (indicating insert)
@@ -1162,7 +1234,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1162
1234
  if (!this._options.wasSourceIModelCopiedToTarget) {
1163
1235
  this.importer.importElement(targetElementProps); // don't need to import if iModel was copied
1164
1236
  }
1165
- this.context.remapElement(sourceElement.id, targetElementProps.id); // targetElementProps.id assigned by importElement
1237
+ if (targetElementProps.id === undefined) {
1238
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "targetElementProps.id should be assigned by importElement");
1239
+ }
1240
+ this.context.remapElement(sourceElement.id, targetElementProps.id);
1166
1241
  // the transformer does not currently 'split' or 'join' any elements, therefore, it does not
1167
1242
  // insert external source aspects because federation guids are sufficient for this.
1168
1243
  // Other transformer subclasses must insert the appropriate aspect (as provided by a TBD API)
@@ -1170,26 +1245,47 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1170
1245
  // physical consolidation is an example of a 'joining' transform
1171
1246
  // FIXME: verify at finalization time that we don't lose provenance on new elements
1172
1247
  // FIXME: make public and improve `initElementProvenance` API for usage by consolidators
1248
+ const provenanceDb = await this.getProvenanceDb();
1173
1249
  if (!this._options.noProvenance) {
1174
1250
  let provenance = this._options.forceExternalSourceAspectProvenance ||
1175
1251
  this._elementsWithExplicitlyTrackedProvenance.has(sourceElement.id)
1176
1252
  ? undefined
1177
1253
  : sourceElement.federationGuid;
1178
1254
  if (!provenance) {
1179
- const aspectProps = this.initElementProvenance(sourceElement.id, targetElementProps.id);
1180
- const foundEsaProps = IModelTransformer.queryScopeExternalSourceAspect(this.provenanceDb, aspectProps);
1255
+ const aspectProps = await this.initElementProvenance(sourceElement.id, targetElementProps.id);
1256
+ const foundEsaProps = await IModelTransformer.queryScopeExternalSourceAspect(provenanceDb, aspectProps);
1181
1257
  if (foundEsaProps === undefined)
1182
- aspectProps.id = this.provenanceDb.elements.insertAspect(aspectProps);
1258
+ aspectProps.id = provenanceDb.elements.insertAspect(aspectProps);
1183
1259
  else {
1184
1260
  // Since initElementProvenance sets a property 'version' on the aspectProps that we wish to persist in the provenanceDb, only grab the id from the foundEsaProps.
1185
1261
  aspectProps.id = foundEsaProps.aspectId;
1186
- this.provenanceDb.elements.updateAspect(aspectProps);
1262
+ provenanceDb.elements.updateAspect(aspectProps);
1187
1263
  }
1188
1264
  provenance = aspectProps;
1189
1265
  }
1190
1266
  this.markLastProvenance(provenance, { isRelationship: false });
1191
1267
  }
1192
1268
  }
1269
+ // In iTwin js 5.x Elements.queryElementIdByCode() uses Code class to query id:
1270
+ // https://github.com/iTwin/itwinjs-core/blob/master/core/backend/src/IModelDb.ts#L2779
1271
+ // Code class constructor trims white spaces from code value.
1272
+ // Custom implementation of queryElementIdByCode() was added to support querying elements with code values that have trailing whitespaces.
1273
+ // It mimicks 4.x implementation: https://github.com/iTwin/itwinjs-core/blob/9c8b394ec3878a39764be81f928fd8b0b9115d31/core/backend/src/IModelDb.ts#L1882
1274
+ async queryElementIdByCode(iModel, code) {
1275
+ if (core_bentley_1.Id64.isInvalid(code.spec))
1276
+ throw new Error("Invalid CodeSpec");
1277
+ if (code.value === undefined)
1278
+ throw new Error("Invalid Code");
1279
+ const query = "SELECT ECInstanceId FROM BisCore:Element WHERE CodeSpec.Id=? AND CodeScope.Id=? AND CodeValue=?";
1280
+ const queryBinder = new core_common_1.QueryBinder()
1281
+ .bindId(1, code.spec)
1282
+ .bindId(2, core_bentley_1.Id64.fromString(code.scope))
1283
+ .bindString(3, code.value);
1284
+ const queryReader = iModel.createQueryReader(query, queryBinder, {
1285
+ usePrimaryConn: true,
1286
+ });
1287
+ return (await queryReader.step()) ? queryReader.current[0] : undefined;
1288
+ }
1193
1289
  /** Override of [IModelExportHandler.onDeleteElement]($transformer) that is called when [IModelExporter]($transformer) detects that an Element has been deleted from the source iModel.
1194
1290
  * This override propagates the delete to the target iModel via [IModelImporter.deleteElement]($transformer).
1195
1291
  */
@@ -1216,7 +1312,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1216
1312
  this.importer.importModel(targetModelProps);
1217
1313
  }
1218
1314
  /** Override of [IModelExportHandler.onDeleteModel]($transformer) that is called when [IModelExporter]($transformer) detects that a [Model]($backend) has been deleted from the source iModel. */
1219
- onDeleteModel(sourceModelId) {
1315
+ async onDeleteModel(sourceModelId) {
1220
1316
  // It is possible and apparently occasionally sensical to delete a model without deleting its underlying element.
1221
1317
  // - If only the model is deleted, [[initFromExternalSourceAspects]] will have already remapped the underlying element since it still exists.
1222
1318
  // - If both were deleted, [[remapDeletedSourceEntities]] will find and remap the deleted element making this operation valid
@@ -1233,19 +1329,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1233
1329
  WHERE ECInstanceId=:targetModelId
1234
1330
  `;
1235
1331
  if (this.exporter.sourceDbChanges?.element.deleteIds.has(sourceModelId)) {
1236
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
1237
- const isDefinitionPartition = this.targetDb.withPreparedStatement(sql, (stmt) => {
1238
- stmt.bindId("targetModelId", targetModelId);
1239
- const val = stmt.step();
1240
- switch (val) {
1241
- case core_bentley_1.DbResult.BE_SQLITE_ROW:
1242
- return true;
1243
- case core_bentley_1.DbResult.BE_SQLITE_DONE:
1244
- return false;
1245
- default:
1246
- (0, core_bentley_1.assert)(false, `unexpected db result: '${JSON.stringify(stmt)}'`);
1247
- }
1332
+ const params = new core_common_1.QueryBinder().bindId("targetModelId", targetModelId);
1333
+ const reader = this.targetDb.createQueryReader(sql, params, {
1334
+ usePrimaryConn: true,
1248
1335
  });
1336
+ const isDefinitionPartition = await reader.step();
1249
1337
  if (isDefinitionPartition) {
1250
1338
  // Skipping model deletion because model's partition will also be deleted.
1251
1339
  // It expects that model will be present and will fail if it's missing.
@@ -1254,7 +1342,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1254
1342
  }
1255
1343
  }
1256
1344
  try {
1257
- this.importer.deleteModel(targetModelId);
1345
+ await this.importer.deleteModel(targetModelId);
1258
1346
  }
1259
1347
  catch (error) {
1260
1348
  const isDeletionProhibitedErr = error instanceof core_common_1.IModelError &&
@@ -1301,41 +1389,24 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1301
1389
  await this.initialize();
1302
1390
  // import DefinitionModels first
1303
1391
  const childDefinitionPartitionSql = `SELECT ECInstanceId FROM ${core_backend_1.DefinitionPartition.classFullName} WHERE Parent.Id=:subjectId`;
1304
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
1305
- await this.sourceDb.withPreparedStatement(childDefinitionPartitionSql,
1306
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
1307
- async (statement) => {
1308
- statement.bindId("subjectId", sourceSubjectId);
1309
- while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
1310
- await this.processModel(statement.getValue(0).getId());
1311
- }
1312
- });
1392
+ const params = new core_common_1.QueryBinder().bindId("subjectId", sourceSubjectId);
1393
+ for await (const row of this.sourceDb.createQueryReader(childDefinitionPartitionSql, params, { usePrimaryConn: true })) {
1394
+ await this.processModel(row.id);
1395
+ }
1313
1396
  // import other partitions next
1314
1397
  const childPartitionSql = `SELECT ECInstanceId FROM ${core_backend_1.InformationPartitionElement.classFullName} WHERE Parent.Id=:subjectId`;
1315
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
1316
- await this.sourceDb.withPreparedStatement(childPartitionSql,
1317
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
1318
- async (statement) => {
1319
- statement.bindId("subjectId", sourceSubjectId);
1320
- while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
1321
- const modelId = statement.getValue(0).getId();
1322
- const model = this.sourceDb.models.getModel(modelId);
1323
- if (!(model instanceof core_backend_1.DefinitionModel)) {
1324
- await this.processModel(modelId);
1325
- }
1398
+ for await (const row of this.sourceDb.createQueryReader(childPartitionSql, params, { usePrimaryConn: true })) {
1399
+ const modelId = row.id;
1400
+ const model = this.sourceDb.models.getModel(modelId);
1401
+ if (!(model instanceof core_backend_1.DefinitionModel)) {
1402
+ await this.processModel(modelId);
1326
1403
  }
1327
- });
1404
+ }
1328
1405
  // recurse into child Subjects
1329
1406
  const childSubjectSql = `SELECT ECInstanceId FROM ${core_backend_1.Subject.classFullName} WHERE Parent.Id=:subjectId`;
1330
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
1331
- await this.sourceDb.withPreparedStatement(childSubjectSql,
1332
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
1333
- async (statement) => {
1334
- statement.bindId("subjectId", sourceSubjectId);
1335
- while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
1336
- await this.processSubjectSubModels(statement.getValue(0).getId());
1337
- }
1338
- });
1407
+ for await (const row of this.sourceDb.createQueryReader(childSubjectSql, params, { usePrimaryConn: true })) {
1408
+ await this.processSubjectSubModels(row.id);
1409
+ }
1339
1410
  }
1340
1411
  /** Transform the specified sourceModel into ModelProps for the target iModel.
1341
1412
  * @param sourceModel The Model from the source iModel to be transformed.
@@ -1351,6 +1422,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1351
1422
  id: targetModeledElementId,
1352
1423
  };
1353
1424
  targetModelProps.id = targetModeledElementId;
1425
+ if (targetModelProps.parentModel === undefined) {
1426
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "targetElementProps must have a defined parentModel");
1427
+ }
1354
1428
  targetModelProps.parentModel = this.context.findTargetElementId(targetModelProps.parentModel);
1355
1429
  return targetModelProps;
1356
1430
  }
@@ -1372,7 +1446,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1372
1446
  * Note that typically, the reverseSyncVersion is saved as the last changeset merged from the branch into master.
1373
1447
  * Setting initializeReverseSyncVersion to true during a forward transformation could overwrite this correct reverseSyncVersion and should only be done during the first transformation between a master and branch iModel.
1374
1448
  */
1375
- updateSynchronizationVersion({ initializeReverseSyncVersion = false, } = {}) {
1449
+ async updateSynchronizationVersion({ initializeReverseSyncVersion = false, } = {}) {
1376
1450
  const shouldSkipSyncVersionUpdate = !initializeReverseSyncVersion &&
1377
1451
  this._sourceChangeDataState !== "has-changes";
1378
1452
  if (shouldSkipSyncVersionUpdate)
@@ -1380,7 +1454,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1380
1454
  nodeAssert(this._targetScopeProvenanceProps);
1381
1455
  const sourceVersion = `${this.sourceDb.changeset.id};${this.sourceDb.changeset.index}`;
1382
1456
  const targetVersion = `${this.targetDb.changeset.id};${this.targetDb.changeset.index}`;
1383
- if (this.isReverseSynchronization) {
1457
+ if (await this.getIsReverseSynchronization()) {
1384
1458
  const oldVersion = this._targetScopeProvenanceProps.jsonProperties.reverseSyncVersion;
1385
1459
  core_bentley_1.Logger.logInfo(loggerCategory, `updating reverse version from ${oldVersion} to ${sourceVersion}`);
1386
1460
  this._targetScopeProvenanceProps.jsonProperties.reverseSyncVersion =
@@ -1400,6 +1474,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1400
1474
  (this._startingChangesetIndices && initializeReverseSyncVersion)) {
1401
1475
  nodeAssert(this.targetDb.changeset.index !== undefined &&
1402
1476
  this._startingChangesetIndices !== undefined, "updateSynchronizationVersion was called without change history");
1477
+ // Store in a local variable, so typescript knows it's defined (due to the assert above)
1478
+ const startingChangesetIndices = this._startingChangesetIndices;
1403
1479
  const jsonProps = this._targetScopeProvenanceProps.jsonProperties;
1404
1480
  core_bentley_1.Logger.logTrace(loggerCategory, `previous pendingReverseSyncChanges: ${jsonProps.pendingReverseSyncChangesetIndices}`);
1405
1481
  core_bentley_1.Logger.logTrace(loggerCategory, `previous pendingSyncChanges: ${jsonProps.pendingSyncChangesetIndices}`);
@@ -1408,7 +1484,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1408
1484
  // Determine which keys to clear and update based on the synchronization direction
1409
1485
  let syncChangesetsToClearKey;
1410
1486
  let syncChangesetsToUpdateKey;
1411
- if (this.isReverseSynchronization) {
1487
+ if (await this.getIsReverseSynchronization()) {
1412
1488
  syncChangesetsToClearKey = pendingReverseSyncChangesetIndicesKey;
1413
1489
  syncChangesetsToUpdateKey = pendingSyncChangesetIndicesKey;
1414
1490
  }
@@ -1420,31 +1496,31 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1420
1496
  // transformation finalization, the work will be saved immediately, otherwise we've
1421
1497
  // just marked this changeset as a synchronization to ignore, and the user can add other
1422
1498
  // stuff to it which would break future synchronizations
1423
- for (let i = this._startingChangesetIndices.target + 1; i <= this.targetDb.changeset.index + 1; i++)
1499
+ for (let i = startingChangesetIndices.target + 1; i <= this.targetDb.changeset.index + 1; i++)
1424
1500
  jsonProps[syncChangesetsToUpdateKey].push(i);
1425
1501
  // Only keep the changeset indices which are greater than the source, this means they haven't been processed yet.
1426
1502
  jsonProps[syncChangesetsToClearKey] = jsonProps[syncChangesetsToClearKey].filter((csIndex) => {
1427
- return csIndex > this._startingChangesetIndices.source;
1503
+ return csIndex > startingChangesetIndices.source;
1428
1504
  });
1429
1505
  // if reverse sync then we may have received provenance changes which should be marked as sync changes
1430
- if (this.isReverseSynchronization) {
1506
+ if (await this.getIsReverseSynchronization()) {
1431
1507
  nodeAssert(this.sourceDb.changeset.index !== undefined, "changeset didn't exist");
1432
- for (let i = this._startingChangesetIndices.source + 1; i <= this.sourceDb.changeset.index + 1; i++)
1508
+ for (let i = startingChangesetIndices.source + 1; i <= this.sourceDb.changeset.index + 1; i++)
1433
1509
  jsonProps.pendingReverseSyncChangesetIndices.push(i);
1434
1510
  }
1435
1511
  core_bentley_1.Logger.logTrace(loggerCategory, `new pendingReverseSyncChanges: ${jsonProps.pendingReverseSyncChangesetIndices}`);
1436
1512
  core_bentley_1.Logger.logTrace(loggerCategory, `new pendingSyncChanges: ${jsonProps.pendingSyncChangesetIndices}`);
1437
1513
  }
1438
- this.provenanceDb.elements.updateAspect({
1514
+ (await this.getProvenanceDb()).elements.updateAspect({
1439
1515
  ...this._targetScopeProvenanceProps,
1440
1516
  jsonProperties: JSON.stringify(this._targetScopeProvenanceProps.jsonProperties),
1441
1517
  });
1442
1518
  this.clearCachedSynchronizationVersion();
1443
1519
  }
1444
1520
  // FIXME<MIKE>: is this necessary when manually using low level transform APIs? (document if so)
1445
- finalizeTransformation() {
1521
+ async finalizeTransformation() {
1446
1522
  this.importer.finalize();
1447
- this.updateSynchronizationVersion({
1523
+ await this.updateSynchronizationVersion({
1448
1524
  initializeReverseSyncVersion: this._isProvenanceInitTransform,
1449
1525
  });
1450
1526
  // TODO: ignore if we remove change cache usage
@@ -1477,22 +1553,23 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1477
1553
  /** Override of [IModelExportHandler.onExportRelationship]($transformer) that imports a relationship into the target iModel when it is exported from the source iModel.
1478
1554
  * This override calls [[onTransformRelationship]] and then [IModelImporter.importRelationship]($transformer) to update the target iModel.
1479
1555
  */
1480
- onExportRelationship(sourceRelationship) {
1481
- const sourceFedGuid = queryElemFedGuid(this.sourceDb, sourceRelationship.sourceId);
1482
- const targetFedGuid = queryElemFedGuid(this.sourceDb, sourceRelationship.targetId);
1556
+ async onExportRelationship(sourceRelationship) {
1557
+ const sourceFedGuid = this.sourceDb.elements.getFederationGuidFromId(sourceRelationship.sourceId);
1558
+ const targetFedGuid = this.sourceDb.elements.getFederationGuidFromId(sourceRelationship.targetId);
1483
1559
  const targetRelationshipProps = this.onTransformRelationship(sourceRelationship);
1484
1560
  const targetRelationshipInstanceId = this.importer.importRelationship(targetRelationshipProps);
1561
+ const provenanceDb = await this.getProvenanceDb();
1485
1562
  if (!this._options.noProvenance &&
1486
1563
  core_bentley_1.Id64.isValid(targetRelationshipInstanceId)) {
1487
1564
  let provenance = !this._options.forceExternalSourceAspectProvenance
1488
1565
  ? sourceFedGuid && targetFedGuid && `${sourceFedGuid}/${targetFedGuid}`
1489
1566
  : undefined;
1490
1567
  if (!provenance) {
1491
- const aspectProps = this.initRelationshipProvenance(sourceRelationship, targetRelationshipInstanceId);
1492
- const foundEsaProps = IModelTransformer.queryScopeExternalSourceAspect(this.provenanceDb, aspectProps);
1568
+ const aspectProps = await this.initRelationshipProvenance(sourceRelationship, targetRelationshipInstanceId);
1569
+ const foundEsaProps = await IModelTransformer.queryScopeExternalSourceAspect(provenanceDb, aspectProps);
1493
1570
  // onExportRelationship doesn't need to call updateAspect if esaProps were found, because relationship provenance doesn't have the same concept of a version as element provenance (which uses last mod time on the elements).
1494
1571
  if (undefined === foundEsaProps) {
1495
- aspectProps.id = this.provenanceDb.elements.insertAspect(aspectProps);
1572
+ aspectProps.id = provenanceDb.elements.insertAspect(aspectProps);
1496
1573
  }
1497
1574
  provenance = aspectProps;
1498
1575
  }
@@ -1502,7 +1579,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1502
1579
  /** Override of [IModelExportHandler.onDeleteRelationship]($transformer) that is called when [IModelExporter]($transformer) detects that a [Relationship]($backend) has been deleted from the source iModel.
1503
1580
  * This override propagates the delete to the target iModel via [IModelImporter.deleteRelationship]($transformer).
1504
1581
  */
1505
- onDeleteRelationship(sourceRelInstanceId) {
1582
+ async onDeleteRelationship(sourceRelInstanceId) {
1506
1583
  nodeAssert(this._deletedSourceRelationshipData, "should be defined at initialization by now");
1507
1584
  const deletedRelData = this._deletedSourceRelationshipData.get(sourceRelInstanceId);
1508
1585
  if (!deletedRelData) {
@@ -1523,7 +1600,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1523
1600
  }
1524
1601
  if (deletedRelData.provenanceAspectId) {
1525
1602
  try {
1526
- this.provenanceDb.elements.deleteAspect(deletedRelData.provenanceAspectId);
1603
+ (await this.getProvenanceDb()).elements.deleteAspect(deletedRelData.provenanceAspectId);
1527
1604
  }
1528
1605
  catch (error) {
1529
1606
  // This aspect may no longer exist if it was deleted at some other point during the transformation. This is fine.
@@ -1534,51 +1611,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1534
1611
  }
1535
1612
  }
1536
1613
  _yieldManager = new core_bentley_1.YieldManager();
1537
- /** Detect Relationship deletes using ExternalSourceAspects in the target iModel and a *brute force* comparison against relationships in the source iModel.
1538
- * @deprecated in 1.x. Don't use this anymore
1539
- * @see [[process]] with [[IModelTransformOptions.argsForProcessChanges]] provided.
1540
- * @note This method is called from [[process]] when [[IModelTransformOptions.argsForProcessChanges]] are undefined, so it only needs to be called directly when processing a subset of an iModel.
1541
- * @throws [[IModelError]] If the required provenance information is not available to detect deletes.
1542
- */
1543
- async detectRelationshipDeletes() {
1544
- if (this.isReverseSynchronization) {
1545
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "Cannot detect deletes when isReverseSynchronization=true");
1546
- }
1547
- const aspectDeleteIds = [];
1548
- const sql = `
1549
- SELECT ECInstanceId, Identifier, JsonProperties
1550
- FROM ${core_backend_1.ExternalSourceAspect.classFullName} aspect
1551
- WHERE aspect.Scope.Id=:scopeId
1552
- AND aspect.Kind=:kind
1553
- `;
1554
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
1555
- await this.targetDb.withPreparedStatement(sql,
1556
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
1557
- async (statement) => {
1558
- statement.bindId("scopeId", this.targetScopeElementId);
1559
- statement.bindString("kind", core_backend_1.ExternalSourceAspect.Kind.Relationship);
1560
- while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
1561
- const sourceRelInstanceId = core_bentley_1.Id64.fromJSON(statement.getValue(1).getString());
1562
- if (undefined ===
1563
- this.sourceDb.relationships.tryGetInstanceProps(core_backend_1.ElementRefersToElements.classFullName, sourceRelInstanceId)) {
1564
- // this function exists only to support some in-imodel transformations, which must
1565
- // use the old (external source aspect) provenance method anyway so we don't need to support
1566
- // new provenance
1567
- const json = JSON.parse(statement.getValue(2).getString());
1568
- const targetRelInstanceId = json.targetRelInstanceId ?? json.provenanceRelInstanceId;
1569
- if (targetRelInstanceId) {
1570
- this.importer.deleteRelationship({
1571
- id: targetRelInstanceId,
1572
- classFullName: core_backend_1.ElementRefersToElements.classFullName,
1573
- });
1574
- }
1575
- aspectDeleteIds.push(statement.getValue(0).getId());
1576
- }
1577
- await this._yieldManager.allowYield();
1578
- }
1579
- });
1580
- this.targetDb.elements.deleteAspect(aspectDeleteIds);
1581
- }
1582
1614
  /** Transform the specified sourceRelationship into RelationshipProps for the target iModel.
1583
1615
  * @param sourceRelationship The Relationship from the source iModel to be transformed.
1584
1616
  * @returns RelationshipProps for the target iModel.
@@ -1589,11 +1621,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1589
1621
  targetRelationshipProps.sourceId = this.context.findTargetElementId(sourceRelationship.sourceId);
1590
1622
  targetRelationshipProps.targetId = this.context.findTargetElementId(sourceRelationship.targetId);
1591
1623
  // TODO: move to cloneRelationship in IModelCloneContext
1592
- sourceRelationship.forEachProperty((propertyName, propertyMetaData) => {
1593
- if (core_common_1.PrimitiveTypeCode.Long === propertyMetaData.primitiveType &&
1594
- "Id" === propertyMetaData.extendedType) {
1595
- targetRelationshipProps[propertyName] =
1596
- this.context.findTargetElementId(sourceRelationship.asAny[propertyName]);
1624
+ sourceRelationship.forEach((propertyName, property) => {
1625
+ if (property.isPrimitive() && "Id" === property.extendedTypeName) {
1626
+ targetRelationshipProps[core_common_1.ECJsNames.toJsName(propertyName)] =
1627
+ this.context.findTargetElementId(sourceRelationship.asAny[core_common_1.ECJsNames.toJsName(propertyName)]);
1597
1628
  }
1598
1629
  });
1599
1630
  return targetRelationshipProps;
@@ -1606,9 +1637,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1606
1637
  /** Override of [IModelExportHandler.onExportElementUniqueAspect]($transformer) that imports an ElementUniqueAspect into the target iModel when it is exported from the source iModel.
1607
1638
  * This override calls [[onTransformElementAspect]] and then [IModelImporter.importElementUniqueAspect]($transformer) to update the target iModel.
1608
1639
  */
1609
- onExportElementUniqueAspect(sourceAspect) {
1610
- const targetAspectProps = this.onTransformElementAspect(sourceAspect);
1611
- if (!this.doAllReferencesExistInTarget(sourceAspect)) {
1640
+ async onExportElementUniqueAspect(sourceAspect) {
1641
+ const targetAspectProps = await this.onTransformElementAspect(sourceAspect);
1642
+ if (!(await this.doAllReferencesExistInTarget(sourceAspect))) {
1612
1643
  this._partiallyCommittedAspectIds.add(sourceAspect.id);
1613
1644
  }
1614
1645
  const targetId = this.importer.importElementUniqueAspect(targetAspectProps);
@@ -1618,16 +1649,16 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1618
1649
  * This override calls [[onTransformElementAspect]] for each ElementMultiAspect and then [IModelImporter.importElementMultiAspects]($transformer) to update the target iModel.
1619
1650
  * @note ElementMultiAspects are handled as a group to make it easier to differentiate between insert, update, and delete.
1620
1651
  */
1621
- onExportElementMultiAspects(sourceAspects) {
1652
+ async onExportElementMultiAspects(sourceAspects) {
1622
1653
  // Transform source ElementMultiAspects into target ElementAspectProps
1623
- const targetAspectPropsArray = sourceAspects.map((srcA) => this.onTransformElementAspect(srcA));
1624
- sourceAspects.forEach((a) => {
1625
- if (!this.doAllReferencesExistInTarget(a)) {
1654
+ const targetAspectPropsArray = sourceAspects.map(async (srcA) => this.onTransformElementAspect(srcA));
1655
+ sourceAspects.forEach(async (a) => {
1656
+ if (!(await this.doAllReferencesExistInTarget(a))) {
1626
1657
  this._partiallyCommittedAspectIds.add(a.id);
1627
1658
  }
1628
1659
  });
1629
1660
  // const targetAspectsToImport = targetAspectPropsArray.filter((targetAspect, i) => hasEntityChanged(sourceAspects[i], targetAspect));
1630
- const targetIds = this.importer.importElementMultiAspects(targetAspectPropsArray, (a) => {
1661
+ const targetIds = this.importer.importElementMultiAspects(await Promise.all(targetAspectPropsArray), (a) => {
1631
1662
  const isExternalSourceAspectFromTransformer = a instanceof core_backend_1.ExternalSourceAspect &&
1632
1663
  a.scope?.id === this.targetScopeElementId;
1633
1664
  return (!this._options.includeSourceProvenance ||
@@ -1642,8 +1673,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1642
1673
  * @returns ElementAspectProps for the target iModel.
1643
1674
  * @note A subclass can override this method to provide custom transform behavior.
1644
1675
  */
1645
- onTransformElementAspect(sourceElementAspect) {
1646
- const targetElementAspectProps = this.context.cloneElementAspect(sourceElementAspect);
1676
+ async onTransformElementAspect(sourceElementAspect) {
1677
+ const targetElementAspectProps = await this.context.cloneElementAspect(sourceElementAspect);
1647
1678
  return targetElementAspectProps;
1648
1679
  }
1649
1680
  /** The directory where schemas will be exported, a random temporary directory */
@@ -1685,7 +1716,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1685
1716
  nodeAssert(schemaFileName.length <= systemMaxPathSegmentSize, "Schema name was still long. This is a bug.");
1686
1717
  this._longNamedSchemasMap.set(schema.name, schemaFileName);
1687
1718
  }
1688
- /* eslint-disable-next-line deprecation/deprecation */
1689
1719
  this.sourceDb.exportSchema({
1690
1720
  schemaName: schema.name,
1691
1721
  outputDirectory: this._schemaExportDir,
@@ -1773,8 +1803,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1773
1803
  this.context.remapElement(sourceSubjectId, targetSubjectId);
1774
1804
  await this.processChildElements(sourceSubjectId);
1775
1805
  await this.processSubjectSubModels(sourceSubjectId);
1776
- this.completePartiallyCommittedElements();
1777
- this.completePartiallyCommittedAspects();
1806
+ await this.completePartiallyCommittedElements();
1807
+ await this.completePartiallyCommittedAspects();
1778
1808
  }
1779
1809
  /** state to prevent reinitialization, @see [[initialize]] */
1780
1810
  _initialized = false;
@@ -1790,11 +1820,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1790
1820
  async initialize() {
1791
1821
  if (this._initialized)
1792
1822
  return;
1793
- this.initScopeProvenance();
1823
+ await this.initScopeProvenance();
1794
1824
  await this._tryInitChangesetData(this._options.argsForProcessChanges);
1795
1825
  await this.context.initialize();
1796
1826
  // need exporter initialized to do remapdeletedsourceentities.
1797
- await this.exporter.initialize(this.getExportInitOpts(this._options.argsForProcessChanges ?? {}));
1827
+ await this.exporter.initialize(await this.getExportInitOpts(this._options.argsForProcessChanges ?? {}));
1798
1828
  // Exporter must be initialized prior to processing changesets in order to properly handle entity recreations (an entity delete followed by an insert of that same entity).
1799
1829
  await this.processChangesets();
1800
1830
  this._initialized = true;
@@ -1806,7 +1836,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1806
1836
  * @returns void
1807
1837
  */
1808
1838
  async processChangesets() {
1809
- this.forEachTrackedElement((sourceElementId, targetElementId) => {
1839
+ await this.forEachTrackedElement((sourceElementId, targetElementId) => {
1810
1840
  this.context.remapElement(sourceElementId, targetElementId);
1811
1841
  });
1812
1842
  if (this.exporter.sourceDbChanges)
@@ -1820,11 +1850,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1820
1850
  this._sourceChangeDataState = "has-changes";
1821
1851
  }
1822
1852
  const relationshipECClassIdsToSkip = new Set();
1823
- for await (const row of this.sourceDb.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementDrivesElement)")) {
1853
+ for await (const row of this.sourceDb.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementDrivesElement)", undefined, { usePrimaryConn: true })) {
1824
1854
  relationshipECClassIdsToSkip.add(row.ECInstanceId);
1825
1855
  }
1826
1856
  const relationshipECClassIds = new Set();
1827
- for await (const row of this.sourceDb.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementRefersToElements)")) {
1857
+ for await (const row of this.sourceDb.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementRefersToElements)", undefined, { usePrimaryConn: true })) {
1828
1858
  relationshipECClassIds.add(row.ECInstanceId);
1829
1859
  }
1830
1860
  // For later use when processing deletes.
@@ -1848,7 +1878,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1848
1878
  disableSchemaCheck: true,
1849
1879
  });
1850
1880
  const csAdaptor = new core_backend_1.ChangesetECAdaptor(csReader);
1851
- const ecChangeUnifier = new core_backend_1.PartialECChangeUnifier();
1881
+ const ecChangeUnifier = new core_backend_1.PartialECChangeUnifier(this.sourceDb);
1852
1882
  while (csAdaptor.step()) {
1853
1883
  ecChangeUnifier.appendFrom(csAdaptor);
1854
1884
  }
@@ -1906,7 +1936,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1906
1936
  async processDeletedOp(change, mapOfDeletedElemIdToScopeEsas, isRelationship, alreadyImportedElementInserts, alreadyImportedModelInserts) {
1907
1937
  // we need a connected iModel with changes to remap elements with deletions
1908
1938
  const notConnectedModel = this.sourceDb.iTwinId === undefined;
1909
- const noChanges = this.synchronizationVersion.index === this.sourceDb.changeset.index &&
1939
+ const noChanges = (await this.getSynchronizationVersion()).index ===
1940
+ this.sourceDb.changeset.index &&
1910
1941
  (this.exporter.sourceDbChanges === undefined ||
1911
1942
  !this.exporter.sourceDbChanges.hasChanges);
1912
1943
  if (notConnectedModel || noChanges)
@@ -1915,7 +1946,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1915
1946
  * if our ChangedECInstance is in the provenanceDb, then we can use the ids we find in the ChangedECInstance to query for ESAs.
1916
1947
  * This is because the ESAs are stored on an element Id thats present in the provenanceDb.
1917
1948
  */
1918
- const changeDataInProvenanceDb = this.sourceDb === this.provenanceDb;
1949
+ const changeDataInProvenanceDb = this.sourceDb === (await this.getProvenanceDb());
1919
1950
  const getTargetIdFromSourceId = async (id) => {
1920
1951
  let identifierValue;
1921
1952
  let element;
@@ -1931,7 +1962,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1931
1962
  this.targetScopeElementId,
1932
1963
  core_backend_1.ExternalSourceAspect.Kind.Element,
1933
1964
  id,
1934
- ]))) {
1965
+ ]), { usePrimaryConn: true })) {
1935
1966
  identifierValue = row.Identifier;
1936
1967
  }
1937
1968
  identifierValue =
@@ -1944,7 +1975,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1944
1975
  }
1945
1976
  // Check for targetId using sourceId's fedguid if we didn't find an esa.
1946
1977
  if (fedGuid) {
1947
- const targetId = this._queryElemIdByFedGuid(this.targetDb, fedGuid);
1978
+ const targetId = this.targetDb.elements.getIdFromFederationGuid(fedGuid);
1948
1979
  return targetId;
1949
1980
  }
1950
1981
  return undefined;
@@ -1957,20 +1988,20 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1957
1988
  const sourceIdOfRelationshipInTarget = await getTargetIdFromSourceId(sourceIdOfRelationshipInSource);
1958
1989
  const targetIdOfRelationshipInTarget = await getTargetIdFromSourceId(targetIdOfRelationshipInSource);
1959
1990
  if (sourceIdOfRelationshipInTarget && targetIdOfRelationshipInTarget) {
1960
- this._deletedSourceRelationshipData.set(changedInstanceId, {
1991
+ this._deletedSourceRelationshipData?.set(changedInstanceId, {
1961
1992
  classFullName: classFullName ?? "",
1962
1993
  sourceIdInTarget: sourceIdOfRelationshipInTarget,
1963
1994
  targetIdInTarget: targetIdOfRelationshipInTarget,
1964
1995
  });
1965
1996
  }
1966
- else if (this.sourceDb === this.provenanceSourceDb) {
1967
- const relProvenance = this._queryProvenanceForRelationship(changedInstanceId, {
1997
+ else if (this.sourceDb === (await this.getProvenanceSourceDb())) {
1998
+ const relProvenance = await this._queryProvenanceForRelationship(changedInstanceId, {
1968
1999
  classFullName: classFullName ?? "",
1969
2000
  sourceId: sourceIdOfRelationshipInSource,
1970
2001
  targetId: targetIdOfRelationshipInSource,
1971
2002
  });
1972
2003
  if (relProvenance && relProvenance.relationshipId)
1973
- this._deletedSourceRelationshipData.set(changedInstanceId, {
2004
+ this._deletedSourceRelationshipData?.set(changedInstanceId, {
1974
2005
  classFullName: classFullName ?? "",
1975
2006
  relId: relProvenance.relationshipId,
1976
2007
  provenanceAspectId: relProvenance.aspectId,
@@ -1979,14 +2010,18 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
1979
2010
  }
1980
2011
  else {
1981
2012
  let targetId = await getTargetIdFromSourceId(changedInstanceId);
1982
- if (targetId === undefined && this.sourceDb === this.provenanceSourceDb) {
1983
- targetId = this._queryProvenanceForElement(changedInstanceId);
2013
+ if (targetId === undefined &&
2014
+ this.sourceDb === (await this.getProvenanceSourceDb())) {
2015
+ targetId = await this._queryProvenanceForElement(changedInstanceId);
1984
2016
  }
1985
2017
  // since we are processing one changeset at a time, we can see local source deletes
1986
2018
  // of entities that were never synced and can be safely ignored
1987
2019
  const deletionNotInTarget = !targetId;
1988
2020
  if (deletionNotInTarget)
1989
2021
  return;
2022
+ if (targetId === undefined) {
2023
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "targetId should be acquired from source id or element provenance");
2024
+ }
1990
2025
  this.context.remapElement(changedInstanceId, targetId);
1991
2026
  // If an entity insert and an entity delete both point to the same entity in target iModel, that means that entity was recreated.
1992
2027
  // In such case an entity update will be triggered and we no longer need to delete the entity.
@@ -2005,7 +2040,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
2005
2040
  this._sourceChangeDataState = "unconnected";
2006
2041
  return;
2007
2042
  }
2008
- const noChanges = this.synchronizationVersion.index === this.sourceDb.changeset.index;
2043
+ const syncVersion = await this.getSynchronizationVersion();
2044
+ const noChanges = syncVersion.index === this.sourceDb.changeset.index;
2009
2045
  if (noChanges) {
2010
2046
  this._sourceChangeDataState = "no-changes";
2011
2047
  this._csFileProps = [];
@@ -2014,34 +2050,29 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
2014
2050
  const startChangeset = "startChangeset" in args ? args.startChangeset : undefined;
2015
2051
  // NOTE: that we do NOT download the changesummary for the last transformed version, we want
2016
2052
  // to ignore those already processed changes
2017
- const startChangesetIndexOrId = startChangeset?.index ??
2018
- startChangeset?.id ??
2019
- this.synchronizationVersion.index + 1;
2053
+ const startChangesetIndexOrId = startChangeset?.index ?? startChangeset?.id ?? syncVersion.index + 1;
2020
2054
  const endChangesetId = this.sourceDb.changeset.id;
2021
2055
  const [startChangesetIndex, endChangesetIndex] = await Promise.all([startChangesetIndexOrId, endChangesetId].map(async (indexOrId) => typeof indexOrId === "number"
2022
2056
  ? indexOrId
2023
- : core_backend_1.BriefcaseManager
2024
- .queryChangeset({
2057
+ : core_backend_1.BriefcaseManager.queryChangeset({
2025
2058
  iModelId: this.sourceDb.iModelId,
2026
- // eslint-disable-next-line deprecation/deprecation
2027
2059
  changeset: { id: indexOrId },
2028
- })
2029
- .then((changeset) => changeset.index)));
2030
- const missingChangesets = startChangesetIndex > this.synchronizationVersion.index + 1;
2060
+ }).then((changeset) => changeset.index)));
2061
+ const missingChangesets = startChangesetIndex > syncVersion.index + 1;
2031
2062
  if (!this._options.argsForProcessChanges
2032
2063
  ?.ignoreMissingChangesetsInSynchronizations &&
2033
- startChangesetIndex !== this.synchronizationVersion.index + 1 &&
2034
- this.synchronizationVersion.index !== -1) {
2064
+ startChangesetIndex !== syncVersion.index + 1 &&
2065
+ syncVersion.index !== -1) {
2035
2066
  throw Error(`synchronization is ${missingChangesets ? "missing changesets" : ""},` +
2036
2067
  " startChangesetId should be" +
2037
2068
  " exactly the first changeset *after* the previous synchronization to not miss data." +
2038
2069
  ` You specified '${startChangesetIndexOrId}' which is changeset #${startChangesetIndex}` +
2039
- ` but the previous synchronization for this targetScopeElement was '${this.synchronizationVersion.id}'` +
2040
- ` which is changeset #${this.synchronizationVersion.index}. The transformer expected` +
2041
- ` #${this.synchronizationVersion.index + 1}.`);
2070
+ ` but the previous synchronization for this targetScopeElem ${syncVersion.id}'` +
2071
+ ` which is changeset #${syncVersion.index}. The transformer expected` +
2072
+ ` #${syncVersion.index + 1}.`);
2042
2073
  }
2043
2074
  nodeAssert(this._targetScopeProvenanceProps, "_targetScopeProvenanceProps should be set by now");
2044
- const changesetsToSkip = this.isReverseSynchronization
2075
+ const changesetsToSkip = (await this.getIsReverseSynchronization())
2045
2076
  ? this._targetScopeProvenanceProps.jsonProperties
2046
2077
  .pendingReverseSyncChangesetIndices
2047
2078
  : this._targetScopeProvenanceProps.jsonProperties
@@ -2109,21 +2140,18 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
2109
2140
  else {
2110
2141
  await this.exporter.exportModel(core_common_1.IModel.repositoryModelId);
2111
2142
  }
2112
- this.completePartiallyCommittedElements();
2143
+ await this.completePartiallyCommittedElements();
2113
2144
  await this.exporter["exportAllAspects"](); // eslint-disable-line @typescript-eslint/dot-notation
2114
- this.completePartiallyCommittedAspects();
2145
+ await this.completePartiallyCommittedAspects();
2115
2146
  await this.exporter.exportRelationships(core_backend_1.ElementRefersToElements.classFullName);
2116
2147
  if (this._options.forceExternalSourceAspectProvenance &&
2117
2148
  this.shouldDetectDeletes()) {
2118
- // eslint-disable-next-line deprecation/deprecation
2119
- await this.detectElementDeletes();
2120
- // eslint-disable-next-line deprecation/deprecation
2121
- await this.detectRelationshipDeletes();
2149
+ core_bentley_1.Logger.logWarning(loggerCategory, "This workflows was deprecated in v1 and is no longer supported");
2122
2150
  }
2123
2151
  if (this._options.optimizeGeometry)
2124
2152
  this.importer.optimizeGeometry(this._options.optimizeGeometry);
2125
2153
  this.importer.computeProjectExtents();
2126
- this.finalizeTransformation();
2154
+ await this.finalizeTransformation();
2127
2155
  }
2128
2156
  /** previous provenance, either a federation guid, a `${sourceFedGuid}/${targetFedGuid}` pair, or required aspect props */
2129
2157
  _lastProvenanceEntityInfo = nullLastProvenanceEntityInfo;
@@ -2151,13 +2179,13 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
2151
2179
  */
2152
2180
  async processChanges(options) {
2153
2181
  // must wait for initialization of synchronization provenance data
2154
- await this.exporter.exportChanges(this.getExportInitOpts(options));
2155
- this.completePartiallyCommittedElements();
2156
- this.completePartiallyCommittedAspects();
2182
+ await this.exporter.exportChanges(await this.getExportInitOpts(options));
2183
+ await this.completePartiallyCommittedElements();
2184
+ await this.completePartiallyCommittedAspects();
2157
2185
  if (this._options.optimizeGeometry)
2158
2186
  this.importer.optimizeGeometry(this._options.optimizeGeometry);
2159
2187
  this.importer.computeProjectExtents();
2160
- this.finalizeTransformation();
2188
+ await this.finalizeTransformation();
2161
2189
  const defaultSaveTargetChanges = () => {
2162
2190
  this.targetDb.saveChanges();
2163
2191
  };
@@ -2166,7 +2194,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
2166
2194
  /** Changeset data must be initialized in order to build correct changeOptions.
2167
2195
  * Call [[IModelTransformer.initialize]] for initialization of synchronization provenance data
2168
2196
  */
2169
- getExportInitOpts(opts) {
2197
+ async getExportInitOpts(opts) {
2170
2198
  if (!this._options.argsForProcessChanges)
2171
2199
  return {};
2172
2200
  const startChangeset = "startChangeset" in opts ? opts.startChangeset : undefined;
@@ -2180,7 +2208,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
2180
2208
  ? { startChangeset }
2181
2209
  : {
2182
2210
  startChangeset: {
2183
- index: this.synchronizationVersion.index + 1,
2211
+ index: (await this.getSynchronizationVersion()).index + 1,
2184
2212
  },
2185
2213
  }),
2186
2214
  };
@@ -2261,11 +2289,11 @@ class TemplateModelCloner extends IModelTransformer {
2261
2289
  return this._sourceIdToTargetIdMap; // return the sourceElementId -> targetElementId Map in case further post-processing is required.
2262
2290
  }
2263
2291
  /** Cloning from a template requires this override of onTransformElement. */
2264
- onTransformElement(sourceElement) {
2292
+ async onTransformElement(sourceElement) {
2265
2293
  const referenceIds = sourceElement.getReferenceIds();
2266
- referenceIds.forEach((referenceId) => {
2294
+ for (const referenceId of referenceIds) {
2267
2295
  // TODO: consider going through all definition elements at once and remapping them to themselves
2268
- if (!core_backend_1.EntityReferences.isValid(this.context.findTargetEntityId(referenceId))) {
2296
+ if (!core_backend_1.EntityReferences.isValid(await this.context.findTargetEntityId(referenceId))) {
2269
2297
  if (this.context.isBetweenIModels) {
2270
2298
  throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, `Remapping for source dependency ${referenceId} not found for target iModel`);
2271
2299
  }
@@ -2280,8 +2308,8 @@ class TemplateModelCloner extends IModelTransformer {
2280
2308
  }
2281
2309
  }
2282
2310
  }
2283
- });
2284
- const targetElementProps = super.onTransformElement(sourceElement);
2311
+ }
2312
+ const targetElementProps = await super.onTransformElement(sourceElement);
2285
2313
  targetElementProps.federationGuid = core_bentley_1.Guid.createValue(); // clone from template should create a new federationGuid
2286
2314
  targetElementProps.code = core_common_1.Code.createEmpty(); // clone from template should not maintain codes
2287
2315
  if (sourceElement instanceof core_backend_1.GeometricElement) {
@@ -2294,23 +2322,9 @@ class TemplateModelCloner extends IModelTransformer {
2294
2322
  targetElementProps.placement = placement;
2295
2323
  }
2296
2324
  }
2297
- this._sourceIdToTargetIdMap.set(sourceElement.id, core_bentley_1.Id64.invalid); // keep track of (source) elementIds from the template model, but the target hasn't been inserted yet
2325
+ this._sourceIdToTargetIdMap?.set(sourceElement.id, core_bentley_1.Id64.invalid); // keep track of (source) elementIds from the template model, but the target hasn't been inserted yet
2298
2326
  return targetElementProps;
2299
2327
  }
2300
2328
  }
2301
2329
  exports.TemplateModelCloner = TemplateModelCloner;
2302
- function queryElemFedGuid(db, elemId) {
2303
- // eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
2304
- return db.withPreparedStatement(`
2305
- SELECT FederationGuid
2306
- FROM bis.Element
2307
- WHERE ECInstanceId=?
2308
- `, (stmt) => {
2309
- stmt.bindId(1, elemId);
2310
- (0, core_bentley_1.assert)(stmt.step() === core_bentley_1.DbResult.BE_SQLITE_ROW);
2311
- const result = stmt.getValue(0).getGuid();
2312
- (0, core_bentley_1.assert)(stmt.step() === core_bentley_1.DbResult.BE_SQLITE_DONE);
2313
- return result;
2314
- });
2315
- }
2316
2330
  //# sourceMappingURL=IModelTransformer.js.map