@itwin/core-backend 5.14.0-dev.2 → 5.14.0-dev.4

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 (59) hide show
  1. package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
  2. package/lib/cjs/BriefcaseManager.js +11 -5
  3. package/lib/cjs/BriefcaseManager.js.map +1 -1
  4. package/lib/cjs/CloudSqlite.d.ts.map +1 -1
  5. package/lib/cjs/CloudSqlite.js +9 -2
  6. package/lib/cjs/CloudSqlite.js.map +1 -1
  7. package/lib/cjs/ECDb.d.ts.map +1 -1
  8. package/lib/cjs/ECDb.js +1 -0
  9. package/lib/cjs/ECDb.js.map +1 -1
  10. package/lib/cjs/IModelDb.d.ts +42 -7
  11. package/lib/cjs/IModelDb.d.ts.map +1 -1
  12. package/lib/cjs/IModelDb.js +175 -74
  13. package/lib/cjs/IModelDb.js.map +1 -1
  14. package/lib/cjs/SchemaSync.d.ts +126 -18
  15. package/lib/cjs/SchemaSync.d.ts.map +1 -1
  16. package/lib/cjs/SchemaSync.js +303 -81
  17. package/lib/cjs/SchemaSync.js.map +1 -1
  18. package/lib/cjs/TxnManager.d.ts.map +1 -1
  19. package/lib/cjs/TxnManager.js +2 -5
  20. package/lib/cjs/TxnManager.js.map +1 -1
  21. package/lib/cjs/internal/SchemaSyncReservations.d.ts.map +1 -1
  22. package/lib/cjs/internal/SchemaSyncReservations.js +13 -3
  23. package/lib/cjs/internal/SchemaSyncReservations.js.map +1 -1
  24. package/lib/esm/BriefcaseManager.d.ts.map +1 -1
  25. package/lib/esm/BriefcaseManager.js +11 -5
  26. package/lib/esm/BriefcaseManager.js.map +1 -1
  27. package/lib/esm/CloudSqlite.d.ts.map +1 -1
  28. package/lib/esm/CloudSqlite.js +10 -3
  29. package/lib/esm/CloudSqlite.js.map +1 -1
  30. package/lib/esm/ECDb.d.ts.map +1 -1
  31. package/lib/esm/ECDb.js +1 -0
  32. package/lib/esm/ECDb.js.map +1 -1
  33. package/lib/esm/IModelDb.d.ts +42 -7
  34. package/lib/esm/IModelDb.d.ts.map +1 -1
  35. package/lib/esm/IModelDb.js +175 -74
  36. package/lib/esm/IModelDb.js.map +1 -1
  37. package/lib/esm/SchemaSync.d.ts +126 -18
  38. package/lib/esm/SchemaSync.d.ts.map +1 -1
  39. package/lib/esm/SchemaSync.js +304 -82
  40. package/lib/esm/SchemaSync.js.map +1 -1
  41. package/lib/esm/TxnManager.d.ts.map +1 -1
  42. package/lib/esm/TxnManager.js +2 -5
  43. package/lib/esm/TxnManager.js.map +1 -1
  44. package/lib/esm/internal/SchemaSyncReservations.d.ts.map +1 -1
  45. package/lib/esm/internal/SchemaSyncReservations.js +13 -3
  46. package/lib/esm/internal/SchemaSyncReservations.js.map +1 -1
  47. package/lib/esm/test/hubaccess/Rebase.test.js +3 -1
  48. package/lib/esm/test/hubaccess/Rebase.test.js.map +1 -1
  49. package/lib/esm/test/hubaccess/SemanticRebase.test.js +29 -28
  50. package/lib/esm/test/hubaccess/SemanticRebase.test.js.map +1 -1
  51. package/lib/esm/test/hubaccess/SemanticRebaseTestUtils.d.ts +3 -0
  52. package/lib/esm/test/hubaccess/SemanticRebaseTestUtils.d.ts.map +1 -0
  53. package/lib/esm/test/hubaccess/SemanticRebaseTestUtils.js +15 -0
  54. package/lib/esm/test/hubaccess/SemanticRebaseTestUtils.js.map +1 -0
  55. package/lib/esm/test/standalone/SchemaSyncDb.test.js +5 -25
  56. package/lib/esm/test/standalone/SchemaSyncDb.test.js.map +1 -1
  57. package/lib/esm/test/standalone/SchemaSyncElementReservation.test.js +1 -0
  58. package/lib/esm/test/standalone/SchemaSyncElementReservation.test.js.map +1 -1
  59. package/package.json +13 -13
@@ -317,7 +317,9 @@ export class IModelDb extends IModel {
317
317
  db[_nativeDb].abandonChanges();
318
318
  db.close();
319
319
  }
320
- catch { }
320
+ catch {
321
+ return;
322
+ }
321
323
  });
322
324
  });
323
325
  }
@@ -383,12 +385,20 @@ export class IModelDb extends IModel {
383
385
  this._codeService = undefined;
384
386
  this[_nativeDb].closeFile();
385
387
  }
388
+ /**
389
+ * Save any unsaved schema changes in this iModel.
390
+ * @internal
391
+ */
386
392
  saveSchemaChanges(args) {
387
393
  if (!this[_nativeDb].hasUnsavedChanges())
388
394
  return;
389
395
  const saveArgs = typeof args === "string" ? { description: args } : args;
390
396
  saveArgs === undefined ? this[_nativeDb].saveChanges() : this[_nativeDb].saveChanges(JSON.stringify(saveArgs));
391
397
  }
398
+ /**
399
+ * Abandon any unsaved schema changes in this iModel.
400
+ * @internal
401
+ */
392
402
  abandonSchemaChanges() {
393
403
  if (!this[_nativeDb].hasUnsavedChanges())
394
404
  return;
@@ -935,7 +945,7 @@ export class IModelDb extends IModel {
935
945
  async dropSchemas(schemaNames) {
936
946
  if (schemaNames.length === 0)
937
947
  return;
938
- if (this[_nativeDb].schemaSyncEnabled())
948
+ if (SchemaSync.isEnabled(this))
939
949
  throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cannot drop schemas when schema sync is enabled");
940
950
  if (this[_nativeDb].hasUnsavedChanges())
941
951
  throw new IModelError(ChangeSetStatus.HasUncommittedChanges, "Cannot drop schemas with unsaved changes");
@@ -1021,26 +1031,34 @@ export class IModelDb extends IModel {
1021
1031
  this.cleanupSnapshot(context.resources);
1022
1032
  }
1023
1033
  }
1024
- /** Shared implementation for importing schemas from file or string. */
1025
- async importSchemasInternal(schemas, options, nativeImportOp) {
1026
- // BriefcaseDb-specific validation checks
1027
- if (this.isBriefcaseDb()) {
1028
- if (this.txns.rebaser.isRebasing) {
1029
- throw new IModelError(IModelStatus.BadRequest, "Cannot import schemas while rebasing");
1030
- }
1031
- if (this.txns.isIndirectChanges) {
1032
- throw new IModelError(IModelStatus.BadRequest, "Cannot import schemas while in an indirect change scope");
1033
- }
1034
- // Additional checks when semantic rebase is enabled
1035
- if (IModelHost.useSemanticRebase) {
1036
- if (this[_nativeDb].hasUnsavedChanges()) {
1037
- throw new IModelError(IModelStatus.BadRequest, "Cannot import schemas with unsaved changes when useSemanticRebase flag is on");
1038
- }
1039
- if (this[_nativeDb].schemaSyncEnabled()) {
1040
- throw new IModelError(IModelStatus.BadRequest, "Cannot import schemas when schema sync is enabled and also useSemanticRebase flag is on");
1041
- }
1034
+ /** Refuse a schema import this briefcase is in no state to run. Shared by [[importSchemas]] and
1035
+ * [[BriefcaseDb.upgradeSchemas]], which reach the native importer by different routes.
1036
+ * @internal
1037
+ */
1038
+ assertCanImportSchemas() {
1039
+ if (!this.isBriefcaseDb())
1040
+ return;
1041
+ if (this.txns.rebaser.isRebasing) {
1042
+ throw new IModelError(IModelStatus.BadRequest, "Cannot import schemas while rebasing");
1043
+ }
1044
+ if (this.txns.isIndirectChanges) {
1045
+ throw new IModelError(IModelStatus.BadRequest, "Cannot import schemas while in an indirect change scope");
1046
+ }
1047
+ // Additional checks when semantic rebase is enabled
1048
+ if (IModelHost.useSemanticRebase) {
1049
+ if (this[_nativeDb].hasUnsavedChanges()) {
1050
+ throw new IModelError(IModelStatus.BadRequest, "Cannot import schemas with unsaved changes when useSemanticRebase flag is on");
1051
+ }
1052
+ if (SchemaSync.isEnabled(this)) {
1053
+ throw new IModelError(IModelStatus.BadRequest, "Cannot import schemas when schema sync is enabled and also useSemanticRebase flag is on");
1042
1054
  }
1043
1055
  }
1056
+ }
1057
+ /** Shared implementation for importing schemas from file or string.
1058
+ * @internal
1059
+ */
1060
+ async importSchemasInternal(schemas, options, nativeImportOp) {
1061
+ this.assertCanImportSchemas();
1044
1062
  if (options?.channelUpgrade) {
1045
1063
  const channelUpgrade = options.channelUpgrade;
1046
1064
  try {
@@ -1058,30 +1076,52 @@ export class IModelDb extends IModel {
1058
1076
  if (options?.schemaImportCallbacks?.preSchemaImportCallback)
1059
1077
  preSchemaImportCallbackResult = await this.preSchemaImportCallback(options.schemaImportCallbacks, { iModel: this, data: options.data, schemaData: schemas });
1060
1078
  const maybeCustomNativeContext = options?.ecSchemaXmlContext?.nativeContext;
1061
- if (this[_nativeDb].schemaSyncEnabled()) {
1062
- await SchemaSync.withLockedAccess(this, { openMode: OpenMode.Readonly, operationName: "schema sync" }, async (syncAccess) => {
1063
- const schemaSyncDbUri = syncAccess.getUri();
1064
- this.saveSchemaChanges();
1065
- try {
1066
- nativeImportOp(schemas, { schemaLockHeld: false, ecSchemaXmlContext: maybeCustomNativeContext, schemaSyncDbUri });
1067
- }
1068
- catch (outerErr) {
1069
- if (DbResult.BE_SQLITE_ERROR_DataTransformRequired === outerErr.errorNumber) {
1070
- this.abandonSchemaChanges();
1071
- if (this[_nativeDb].getITwinId() !== Guid.empty)
1072
- await this.acquireSchemaLock();
1073
- try {
1074
- nativeImportOp(schemas, { schemaLockHeld: true, ecSchemaXmlContext: maybeCustomNativeContext, schemaSyncDbUri });
1075
- }
1076
- catch (innerErr) {
1077
- throw new IModelError(innerErr.errorNumber, innerErr.message);
1078
- }
1079
+ if (SchemaSync.isEnabled(this)) {
1080
+ // The shared lock lets concurrent updates through while blocking anyone taking the exclusive lock for an upgrade.
1081
+ if (this[_nativeDb].getITwinId() !== Guid.empty)
1082
+ await this.locks.acquireLocks({ shared: IModel.repositoryModelId });
1083
+ // The native import adopts rows from the attached SchemaSyncDb. Attach and detach commit
1084
+ // implicitly, so abandoning changes cannot undo the adoption. A later failure must roll the
1085
+ // briefcase back before those ec_ rows can be pushed.
1086
+ let txnBeforeAdopt;
1087
+ // Set once the caller's own work is committed. Getting a token or the container write lock happens
1088
+ // before that, and abandonSchemaChanges discards every unsaved change rather than only this import's,
1089
+ // so abandoning on a failure from that window would throw away work the import never touched.
1090
+ let callerChangesSaved = false;
1091
+ try {
1092
+ await SchemaSync.withLockedAccess(this, { openMode: OpenMode.Readonly, operationName: "schema sync" }, async (syncAccess) => {
1093
+ const schemaSyncDbUri = syncAccess.getUri();
1094
+ this.saveSchemaChanges();
1095
+ callerChangesSaved = true;
1096
+ // After saveSchemaChanges, so rolling back cannot reach the caller's own work.
1097
+ if (this.isBriefcaseDb())
1098
+ txnBeforeAdopt = this.txns.getCurrentTxnId();
1099
+ try {
1100
+ nativeImportOp(schemas, { schemaLockHeld: false, ecSchemaXmlContext: maybeCustomNativeContext, schemaSyncDbUri });
1079
1101
  }
1080
- else {
1081
- throw new IModelError(outerErr.errorNumber, outerErr.message);
1102
+ catch (err) {
1103
+ if (SchemaSync.requiresUpgrade(err))
1104
+ throw new IModelError(err.errorNumber, `${err.message} - this schema change needs the upgrade path. Use BriefcaseDb.upgradeSchemas, which takes the exclusive schema lock and pushes the result.`);
1105
+ throw new IModelError(err.errorNumber, err.message);
1082
1106
  }
1107
+ });
1108
+ }
1109
+ catch (err) {
1110
+ if (callerChangesSaved)
1111
+ this.abandonSchemaChanges();
1112
+ if (this.isBriefcaseDb() && undefined !== txnBeforeAdopt && this.txns.getCurrentTxnId() !== txnBeforeAdopt) {
1113
+ // Saving the adopt's txn started a new undo session, because schema changes are kept out of the
1114
+ // user's undo stack, so this reverses across that boundary. The changes themselves reverse like
1115
+ // any other - a rebase does it to every local txn on every pull. It does not reverse DDL: a table
1116
+ // or column the adopt created stays behind with no ec_ row describing it, which is the state a
1117
+ // briefcase already tolerates, and the next successful import finds the table up to date and
1118
+ // carries on. Cancel rather than reverse, so the rows cannot come back through undo either.
1119
+ const status = this[_nativeDb].cancelTo(txnBeforeAdopt, true);
1120
+ if (IModelStatus.Success !== status)
1121
+ Logger.logError(loggerCategory, `Failed to roll back the adopted schema rows after a failed import: ${IModelStatus[status] ?? status}`);
1083
1122
  }
1084
- });
1123
+ throw err;
1124
+ }
1085
1125
  }
1086
1126
  else {
1087
1127
  const nativeImportOptions = {
@@ -2846,7 +2886,9 @@ function processSchemaWriteStatus(status) {
2846
2886
  try {
2847
2887
  props.push(imodel.elements.getElementProps(id));
2848
2888
  }
2849
- catch { }
2889
+ catch {
2890
+ return;
2891
+ }
2850
2892
  });
2851
2893
  return props;
2852
2894
  }
@@ -2869,7 +2911,9 @@ function processSchemaWriteStatus(status) {
2869
2911
  break;
2870
2912
  }
2871
2913
  }
2872
- catch { }
2914
+ catch {
2915
+ continue;
2916
+ }
2873
2917
  }
2874
2918
  return finished;
2875
2919
  }
@@ -3079,9 +3123,6 @@ export class BriefcaseDb extends IModelDb {
3079
3123
  get isBriefcase() { return true; }
3080
3124
  /* the BriefcaseId of the briefcase opened with this BriefcaseDb */
3081
3125
  briefcaseId;
3082
- _skipSyncSchemasOnPullAndPush;
3083
- /** @internal */
3084
- get skipSyncSchemasOnPullAndPush() { return this._skipSyncSchemasOnPullAndPush ?? false; }
3085
3126
  /**
3086
3127
  * Event raised just before a BriefcaseDb is opened. Supplies the arguments that will be used to open the BriefcaseDb.
3087
3128
  * Throw an exception to stop the open.
@@ -3164,6 +3205,8 @@ export class BriefcaseDb extends IModelDb {
3164
3205
  }
3165
3206
  constructor(args) {
3166
3207
  super({ ...args, changeset: args.nativeDb.getCurrentChangeset() });
3208
+ // Native invokes this private callback dynamically through the object registered by IModelDb.
3209
+ void this.onChangesetConflict;
3167
3210
  this._openMode = args.openMode;
3168
3211
  this.briefcaseId = args.briefcaseId;
3169
3212
  this.makeLockControl();
@@ -3176,26 +3219,33 @@ export class BriefcaseDb extends IModelDb {
3176
3219
  wasChanges = nativeDb.hasPendingTxns();
3177
3220
  nativeDb.closeFile();
3178
3221
  };
3179
- const isSchemaSyncEnabled = await withBriefcaseDb(briefcase, async (db) => {
3180
- await SchemaSync.pull(db);
3181
- return db[_nativeDb].schemaSyncEnabled();
3182
- });
3183
- if (isSchemaSyncEnabled) {
3222
+ const push = async () => {
3223
+ if (wasChanges)
3224
+ await withBriefcaseDb(briefcase, async (db) => db.pushChanges({ ...briefcase, description, retainLocks: true }));
3225
+ };
3226
+ // Asked of the closed file, so an iModel without schema sync never gets opened for this.
3227
+ if (SchemaSync.isEnabled(briefcase)) {
3228
+ await withBriefcaseDb(briefcase, async (db) => SchemaSync.updateDbSchema(db));
3184
3229
  await SchemaSync.withLockedAccess(briefcase, { openMode: OpenMode.Readonly, operationName: "schema sync" }, async (syncAccess) => {
3185
3230
  const schemaSyncDbUri = syncAccess.getUri();
3186
3231
  executeUpgrade();
3232
+ // Overwriting advances the SchemaSync data version, so a no-op upgrade must not call it.
3233
+ if (!wasChanges)
3234
+ return;
3187
3235
  await withBriefcaseDb(briefcase, async (db) => {
3188
- db[_nativeDb].schemaSyncPush(schemaSyncDbUri);
3236
+ db[_nativeDb].schemaSyncOverwrite(schemaSyncDbUri);
3189
3237
  db[_nativeDb].saveChanges();
3190
3238
  });
3191
- syncAccess.synchronizeWithCloud();
3239
+ // Publish the sync db before the changeset while retaining both locks.
3240
+ syncAccess.closeDb();
3241
+ await syncAccess.container.uploadChanges();
3242
+ await push();
3192
3243
  });
3193
3244
  }
3194
3245
  else {
3195
3246
  executeUpgrade();
3247
+ await push();
3196
3248
  }
3197
- if (wasChanges)
3198
- await withBriefcaseDb(briefcase, async (db) => db.pushChanges({ ...briefcase, description, retainLocks: true }));
3199
3249
  }
3200
3250
  /** Upgrades the schemas in the iModel based on the current version of the software. Follows a sequence of operations -
3201
3251
  * * Acquires a schema lock to prevent other users from making any other changes while upgrade is happening
@@ -3215,6 +3265,22 @@ export class BriefcaseDb extends IModelDb {
3215
3265
  // - push changes
3216
3266
  // - release schema lock
3217
3267
  // good thing computers are fast. Fortunately upgrading should be rare (and the push time will dominate anyway.) Don't try to optimize any of this away.
3268
+ // schemaSyncOverwrite can discard unpushed imports, so hold the exclusive lock across both upgrades.
3269
+ if (SchemaSync.isEnabled(briefcase)) {
3270
+ try {
3271
+ await withBriefcaseDb(briefcase, async (db) => {
3272
+ if (!db.locks.isServerBased)
3273
+ throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cannot upgrade schemas with SchemaSync without server-based locking");
3274
+ await db.acquireSchemaLock();
3275
+ });
3276
+ await this.doUpgrade(briefcase, { profile: ProfileOptions.Upgrade, schemaLockHeld: true }, "Upgraded profile");
3277
+ await this.doUpgrade(briefcase, { domain: DomainOptions.Upgrade, schemaLockHeld: true }, "Upgraded domain schemas");
3278
+ }
3279
+ finally {
3280
+ await withBriefcaseDb(briefcase, async (db) => db.locks[_releaseAllLocks]());
3281
+ }
3282
+ return;
3283
+ }
3218
3284
  try {
3219
3285
  await this.doUpgrade(briefcase, { profile: ProfileOptions.Upgrade }, "Upgraded profile");
3220
3286
  }
@@ -3515,8 +3581,7 @@ export class BriefcaseDb extends IModelDb {
3515
3581
  async pullChanges(arg) {
3516
3582
  await this.executeWritable(async () => {
3517
3583
  await BriefcaseManager.pullAndApplyChangesets(this, arg ?? {});
3518
- if (!this.skipSyncSchemasOnPullAndPush)
3519
- await SchemaSync.pull(this);
3584
+ SchemaSync.updateDbSchema(this);
3520
3585
  this.initializeIModelDb("pullMerge");
3521
3586
  });
3522
3587
  // If this pull enabled or disabled SchemaSync for this briefcase, its reservations must now be re-initialized
@@ -3601,24 +3666,10 @@ export class BriefcaseDb extends IModelDb {
3601
3666
  if (nativeDb.hasPendingTxns()) {
3602
3667
  throw new IModelError(ChangeSetStatus.HasLocalChanges, "Cannot revert with pending txns");
3603
3668
  }
3604
- const skipSchemaSyncPull = async (func) => {
3605
- if (nativeDb.schemaSyncEnabled()) {
3606
- this._skipSyncSchemasOnPullAndPush = true;
3607
- try {
3608
- return await func();
3609
- }
3610
- finally {
3611
- this._skipSyncSchemasOnPullAndPush = undefined;
3612
- }
3613
- }
3614
- else {
3615
- return func();
3616
- }
3617
- };
3618
3669
  this.clearCaches();
3619
- await skipSchemaSyncPull(async () => this.pullChanges({ ...arg, toIndex: undefined }));
3670
+ await this.pullChanges({ ...arg, toIndex: undefined });
3620
3671
  await this.acquireSchemaLock();
3621
- if (nativeDb.schemaSyncEnabled()) {
3672
+ if (SchemaSync.isEnabled(this)) {
3622
3673
  arg.skipSchemaChanges = true;
3623
3674
  }
3624
3675
  // The native side enables file-based txns during revert. Restore the original setting afterward.
@@ -3639,7 +3690,7 @@ export class BriefcaseDb extends IModelDb {
3639
3690
  pushRetryDelay: arg.pushRetryDelay,
3640
3691
  retainLocks: arg.retainLocks,
3641
3692
  };
3642
- await skipSchemaSyncPull(async () => this.pushChanges(pushArgs));
3693
+ await this.pushChanges(pushArgs);
3643
3694
  this.clearCaches();
3644
3695
  }
3645
3696
  catch (err) {
@@ -3716,6 +3767,56 @@ export class BriefcaseDb extends IModelDb {
3716
3767
  this.txns._onChangesPushed(this.changeset);
3717
3768
  BriefcaseManager.deleteRebaseFolders(this);
3718
3769
  }
3770
+ /** Import schemas while allowing changes that move or delete data.
3771
+ *
3772
+ * Takes the exclusive schema lock, requires the briefcase to be at the timeline tip, imports, updates the sync db, and pushes the result.
3773
+ * @note The briefcase must have no local changes.
3774
+ * @see [[BriefcaseDb.upgradeSchemas]] (static) for upgrading the software's profile and domain schemas.
3775
+ * @alpha
3776
+ */
3777
+ async upgradeSchemas(schemaFileNames, arg) {
3778
+ return this.upgradeSchemasInternal(schemaFileNames, arg, (schemas, importOptions) => this[_nativeDb].importSchemas(schemas, importOptions));
3779
+ }
3780
+ /** The [[importSchemaStrings]] counterpart of [[upgradeSchemas]].
3781
+ * @alpha
3782
+ */
3783
+ async upgradeSchemaStrings(serializedXmlSchemas, arg) {
3784
+ return this.upgradeSchemasInternal(serializedXmlSchemas, arg, (schemas, importOptions) => this[_nativeDb].importXmlSchemas(schemas, importOptions));
3785
+ }
3786
+ async upgradeSchemasInternal(schemas, arg, nativeImportOp) {
3787
+ if (schemas.length === 0)
3788
+ return;
3789
+ this.assertCanImportSchemas();
3790
+ if (this[_nativeDb].hasUnsavedChanges() || this.txns.hasLocalChanges)
3791
+ throw new IModelError(ChangeSetStatus.HasLocalChanges, "Cannot upgrade schemas while there are local changes");
3792
+ if (SchemaSync.isEnabled(this) && !this.locks.isServerBased)
3793
+ throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cannot upgrade schemas with SchemaSync without server-based locking");
3794
+ await this.acquireSchemaLock();
3795
+ await this.pullChanges({ accessToken: arg.accessToken });
3796
+ if (!SchemaSync.isEnabled(this)) {
3797
+ await this.importSchemasInternal(schemas, { ecSchemaXmlContext: arg.ecSchemaXmlContext }, nativeImportOp);
3798
+ await this.pushChanges(arg);
3799
+ return;
3800
+ }
3801
+ await SchemaSync.withLockedAccess(this, { openMode: OpenMode.Readonly, operationName: "schema upgrade" }, async (syncAccess) => {
3802
+ this.saveSchemaChanges();
3803
+ try {
3804
+ nativeImportOp(schemas, {
3805
+ schemaLockHeld: true,
3806
+ ecSchemaXmlContext: arg.ecSchemaXmlContext?.nativeContext,
3807
+ schemaSyncDbUri: syncAccess.getUri(),
3808
+ });
3809
+ }
3810
+ catch (err) {
3811
+ this.abandonSchemaChanges();
3812
+ throw new IModelError(err.errorNumber, err.message);
3813
+ }
3814
+ this.clearCaches();
3815
+ syncAccess.closeDb();
3816
+ await syncAccess.container.uploadChanges();
3817
+ await this.pushChanges(arg);
3818
+ });
3819
+ }
3719
3820
  close(options) {
3720
3821
  if (this.isBriefcase && this.isOpen && !this.isReadonly && this.txns.rebaser.inProgress()) {
3721
3822
  this[_nativeDb].abandonChanges();