@itwin/core-backend 5.14.0-dev.2 → 5.14.0-dev.21
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.
- package/CHANGELOG.md +53 -1
- package/lib/cjs/BriefcaseManager.d.ts +6 -0
- package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
- package/lib/cjs/BriefcaseManager.js +12 -6
- package/lib/cjs/BriefcaseManager.js.map +1 -1
- package/lib/cjs/ChangesetReader.d.ts +1 -1
- package/lib/cjs/ChangesetReader.d.ts.map +1 -1
- package/lib/cjs/ChangesetReader.js +3 -2
- package/lib/cjs/ChangesetReader.js.map +1 -1
- package/lib/cjs/ChangesetReaderTypes.d.ts +6 -2
- package/lib/cjs/ChangesetReaderTypes.d.ts.map +1 -1
- package/lib/cjs/ChangesetReaderTypes.js +3 -0
- package/lib/cjs/ChangesetReaderTypes.js.map +1 -1
- package/lib/cjs/CloudSqlite.d.ts.map +1 -1
- package/lib/cjs/CloudSqlite.js +9 -2
- package/lib/cjs/CloudSqlite.js.map +1 -1
- package/lib/cjs/ECDb.d.ts +52 -0
- package/lib/cjs/ECDb.d.ts.map +1 -1
- package/lib/cjs/ECDb.js +52 -0
- package/lib/cjs/ECDb.js.map +1 -1
- package/lib/cjs/Element.d.ts +1 -1
- package/lib/cjs/Element.d.ts.map +1 -1
- package/lib/cjs/Element.js +7 -4
- package/lib/cjs/Element.js.map +1 -1
- package/lib/cjs/Entity.d.ts +3 -3
- package/lib/cjs/Entity.js +3 -3
- package/lib/cjs/Entity.js.map +1 -1
- package/lib/cjs/IModelDb.d.ts +83 -35
- package/lib/cjs/IModelDb.d.ts.map +1 -1
- package/lib/cjs/IModelDb.js +203 -89
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/cjs/IpcHost.d.ts.map +1 -1
- package/lib/cjs/IpcHost.js +51 -17
- package/lib/cjs/IpcHost.js.map +1 -1
- package/lib/cjs/SchemaSync.d.ts +126 -18
- package/lib/cjs/SchemaSync.d.ts.map +1 -1
- package/lib/cjs/SchemaSync.js +303 -81
- package/lib/cjs/SchemaSync.js.map +1 -1
- package/lib/cjs/SqliteChangesetReader.d.ts +16 -8
- package/lib/cjs/SqliteChangesetReader.d.ts.map +1 -1
- package/lib/cjs/SqliteChangesetReader.js +11 -1
- package/lib/cjs/SqliteChangesetReader.js.map +1 -1
- package/lib/cjs/SynchronousChannel.d.ts +11 -1
- package/lib/cjs/SynchronousChannel.d.ts.map +1 -1
- package/lib/cjs/SynchronousChannel.js.map +1 -1
- package/lib/cjs/TxnManager.d.ts.map +1 -1
- package/lib/cjs/TxnManager.js +20 -13
- package/lib/cjs/TxnManager.js.map +1 -1
- package/lib/cjs/internal/IntegrityCheck.d.ts +57 -0
- package/lib/cjs/internal/IntegrityCheck.d.ts.map +1 -1
- package/lib/cjs/internal/IntegrityCheck.js.map +1 -1
- package/lib/cjs/internal/SchemaSyncReservations.d.ts.map +1 -1
- package/lib/cjs/internal/SchemaSyncReservations.js +13 -3
- package/lib/cjs/internal/SchemaSyncReservations.js.map +1 -1
- package/lib/esm/BriefcaseManager.d.ts +6 -0
- package/lib/esm/BriefcaseManager.d.ts.map +1 -1
- package/lib/esm/BriefcaseManager.js +12 -6
- package/lib/esm/BriefcaseManager.js.map +1 -1
- package/lib/esm/ChangesetReader.d.ts +1 -1
- package/lib/esm/ChangesetReader.d.ts.map +1 -1
- package/lib/esm/ChangesetReader.js +3 -2
- package/lib/esm/ChangesetReader.js.map +1 -1
- package/lib/esm/ChangesetReaderTypes.d.ts +6 -2
- package/lib/esm/ChangesetReaderTypes.d.ts.map +1 -1
- package/lib/esm/ChangesetReaderTypes.js +3 -0
- package/lib/esm/ChangesetReaderTypes.js.map +1 -1
- package/lib/esm/CloudSqlite.d.ts.map +1 -1
- package/lib/esm/CloudSqlite.js +10 -3
- package/lib/esm/CloudSqlite.js.map +1 -1
- package/lib/esm/ECDb.d.ts +52 -0
- package/lib/esm/ECDb.d.ts.map +1 -1
- package/lib/esm/ECDb.js +52 -0
- package/lib/esm/ECDb.js.map +1 -1
- package/lib/esm/Element.d.ts +1 -1
- package/lib/esm/Element.d.ts.map +1 -1
- package/lib/esm/Element.js +7 -4
- package/lib/esm/Element.js.map +1 -1
- package/lib/esm/Entity.d.ts +3 -3
- package/lib/esm/Entity.js +3 -3
- package/lib/esm/Entity.js.map +1 -1
- package/lib/esm/IModelDb.d.ts +83 -35
- package/lib/esm/IModelDb.d.ts.map +1 -1
- package/lib/esm/IModelDb.js +203 -89
- package/lib/esm/IModelDb.js.map +1 -1
- package/lib/esm/IpcHost.d.ts.map +1 -1
- package/lib/esm/IpcHost.js +52 -18
- package/lib/esm/IpcHost.js.map +1 -1
- package/lib/esm/SchemaSync.d.ts +126 -18
- package/lib/esm/SchemaSync.d.ts.map +1 -1
- package/lib/esm/SchemaSync.js +304 -82
- package/lib/esm/SchemaSync.js.map +1 -1
- package/lib/esm/SqliteChangesetReader.d.ts +16 -8
- package/lib/esm/SqliteChangesetReader.d.ts.map +1 -1
- package/lib/esm/SqliteChangesetReader.js +11 -1
- package/lib/esm/SqliteChangesetReader.js.map +1 -1
- package/lib/esm/SynchronousChannel.d.ts +11 -1
- package/lib/esm/SynchronousChannel.d.ts.map +1 -1
- package/lib/esm/SynchronousChannel.js.map +1 -1
- package/lib/esm/TxnManager.d.ts.map +1 -1
- package/lib/esm/TxnManager.js +21 -14
- package/lib/esm/TxnManager.js.map +1 -1
- package/lib/esm/internal/IntegrityCheck.d.ts +57 -0
- package/lib/esm/internal/IntegrityCheck.d.ts.map +1 -1
- package/lib/esm/internal/IntegrityCheck.js.map +1 -1
- package/lib/esm/internal/SchemaSyncReservations.d.ts.map +1 -1
- package/lib/esm/internal/SchemaSyncReservations.js +13 -3
- package/lib/esm/internal/SchemaSyncReservations.js.map +1 -1
- package/lib/esm/test/annotations/FrameGeometry.test.js +1 -1
- package/lib/esm/test/annotations/FrameGeometry.test.js.map +1 -1
- package/lib/esm/test/ecdb/CSVImport.test.d.ts +2 -0
- package/lib/esm/test/ecdb/CSVImport.test.d.ts.map +1 -0
- package/lib/esm/test/ecdb/CSVImport.test.js +186 -0
- package/lib/esm/test/ecdb/CSVImport.test.js.map +1 -0
- package/lib/esm/test/ecdb/ConcurrentQuery.test.js +19 -2
- package/lib/esm/test/ecdb/ConcurrentQuery.test.js.map +1 -1
- package/lib/esm/test/ecsql/dataset/ECSqlDatasets.d.ts.map +1 -1
- package/lib/esm/test/ecsql/dataset/ECSqlDatasets.js +7 -0
- package/lib/esm/test/ecsql/dataset/ECSqlDatasets.js.map +1 -1
- package/lib/esm/test/ecsql/src/ECSqlTestRunner.test.js +86 -7
- package/lib/esm/test/ecsql/src/ECSqlTestRunner.test.js.map +1 -1
- package/lib/esm/test/hubaccess/BriefcaseManager.test.js +65 -1
- package/lib/esm/test/hubaccess/BriefcaseManager.test.js.map +1 -1
- package/lib/esm/test/hubaccess/Rebase.test.js +3 -1
- package/lib/esm/test/hubaccess/Rebase.test.js.map +1 -1
- package/lib/esm/test/hubaccess/SemanticRebase.test.js +218 -28
- package/lib/esm/test/hubaccess/SemanticRebase.test.js.map +1 -1
- package/lib/esm/test/hubaccess/SemanticRebaseTestUtils.d.ts +3 -0
- package/lib/esm/test/hubaccess/SemanticRebaseTestUtils.d.ts.map +1 -0
- package/lib/esm/test/hubaccess/SemanticRebaseTestUtils.js +15 -0
- package/lib/esm/test/hubaccess/SemanticRebaseTestUtils.js.map +1 -0
- package/lib/esm/test/imodel/IModelElements.test.js +5 -0
- package/lib/esm/test/imodel/IModelElements.test.js.map +1 -1
- package/lib/esm/test/standalone/ChangesetReader.test.js +111 -2
- package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
- package/lib/esm/test/standalone/DgnDomainMergeConflict.test.js +61 -0
- package/lib/esm/test/standalone/DgnDomainMergeConflict.test.js.map +1 -1
- package/lib/esm/test/standalone/SQLiteDb.test.d.ts.map +1 -1
- package/lib/esm/test/standalone/SQLiteDb.test.js +116 -0
- package/lib/esm/test/standalone/SQLiteDb.test.js.map +1 -1
- package/lib/esm/test/standalone/SchemaSyncDb.test.js +5 -25
- package/lib/esm/test/standalone/SchemaSyncDb.test.js.map +1 -1
- package/lib/esm/test/standalone/SchemaSyncElementReservation.test.js +1 -0
- package/lib/esm/test/standalone/SchemaSyncElementReservation.test.js.map +1 -1
- package/package.json +13 -13
package/lib/esm/IModelDb.js
CHANGED
|
@@ -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;
|
|
@@ -431,24 +441,30 @@ export class IModelDb extends IModel {
|
|
|
431
441
|
}
|
|
432
442
|
/**
|
|
433
443
|
* Performs integrity checks on this iModel.
|
|
434
|
-
* Types of integrity checks that can be performed are:
|
|
435
444
|
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
445
|
+
* The quick check runs the ten checks below other than Missing Child Rows. It reports pass/fail for each check
|
|
446
|
+
* based on the first problem found, without returning problem details. The specific checks return all problem rows.
|
|
447
|
+
* Selecting both quickCheck and specific checks repeats the selected work in both modes.
|
|
448
|
+
*
|
|
449
|
+
* The checks are:
|
|
450
|
+
* - Data Columns Check: Checks for missing nonvirtual physical columns in mapped data tables. It does not compare column types.
|
|
451
|
+
* - EC Profile Check: Checks expected EC profile tables, indexes, and iModel triggers for missing or mismatching SQL definitions. It does not check be_* tables.
|
|
452
|
+
* - Navigation Class Ids Check: Checks that each non-null persisted RelECClassId is the declared relationship class or one of its derived classes.
|
|
453
|
+
* - Navigation Ids Check: Checks that a navigation property's non-null .Id (the referenced instance's ECInstanceId) resolves to a row in the first relationship constraint class in the navigation direction, including derived classes.
|
|
454
|
+
* - Linktable Foreign Key Class Ids Check: Checks that SourceECClassId and TargetECClassId have matching meta.ECClassDef entries. This checks class existence only, not endpoint-class validity.
|
|
455
|
+
* - Linktable Foreign Key Ids Check: Checks SourceECInstanceId and TargetECInstanceId for null or an unresolved row in the first endpoint constraint class, including derived classes.
|
|
456
|
+
* - Class Ids Check: Checks persisted ECClassId values for a corresponding class definition. This checks class existence, not whether the class belongs in that table.
|
|
457
|
+
* - Data Schema Check: Checks for missing mapped data tables and indexes. Results report object names and types, not DDL differences.
|
|
458
|
+
* - Schema Load Check: Checks whether the schema manager can load each schema recorded in the database. A problem reports the schema name without a reason.
|
|
459
|
+
* - Missing Child Rows Check: Checks whether each existing bis_Element row has all required mapped child-table rows.
|
|
460
|
+
* - Diverged Property Maps Check: Checks whether an inherited property maps to different columns for a base and derived class under the same physical table root.
|
|
438
461
|
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
* - Navigation Class Ids Check: Checks if RelClassId of a Navigation property is a valid ECClassId. It does not check the value to match the relationship class.
|
|
443
|
-
* - Navigation Ids Check: Checks if Id of a Navigation property matches a valid row primary class.
|
|
444
|
-
* - Linktable Foreign Key Class Ids Check: Checks if SourceECClassId or TargetECClassId of a link table matches a valid ECClassId.
|
|
445
|
-
* - Linktable Foreign Key Ids Check: Checks if SourceECInstanceId or TargetECInstanceId of a link table matches a valid row in primary class.
|
|
446
|
-
* - Class Ids Check: Checks persisted ECClassId in all data tables and makes sure they are valid.
|
|
447
|
-
* - Data Schema Check: Checks if all the required data tables and indexes exist for mapped classes. Issues are returned as a list of tables/columns which were not found or have different DDL.
|
|
448
|
-
* - Schema Load Check: Checks if all schemas can be loaded into memory.
|
|
449
|
-
* - Missing Child Rows Check: Checks if all child rows have a corresponding parent row.
|
|
462
|
+
* If no options are provided, or if quickCheck is false and no specific check is enabled, a quick check is performed by default.
|
|
463
|
+
* The checks are read-only and use the current primary connection. They have no callbacks, and the native operation
|
|
464
|
+
* blocks the backend while each check runs.
|
|
450
465
|
*
|
|
451
|
-
* @
|
|
466
|
+
* @see [ECSQL integrity checks]($docs/learning/ECSqlReference/Pragmas.md#pragma-integrity_check-experimental)
|
|
467
|
+
* @param options Options specifying which integrity checks to perform.
|
|
452
468
|
* @returns An array of integrity check results.
|
|
453
469
|
* @throws [[IModelError]] if the iModel is not open.
|
|
454
470
|
* @beta
|
|
@@ -935,7 +951,7 @@ export class IModelDb extends IModel {
|
|
|
935
951
|
async dropSchemas(schemaNames) {
|
|
936
952
|
if (schemaNames.length === 0)
|
|
937
953
|
return;
|
|
938
|
-
if (
|
|
954
|
+
if (SchemaSync.isEnabled(this))
|
|
939
955
|
throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cannot drop schemas when schema sync is enabled");
|
|
940
956
|
if (this[_nativeDb].hasUnsavedChanges())
|
|
941
957
|
throw new IModelError(ChangeSetStatus.HasUncommittedChanges, "Cannot drop schemas with unsaved changes");
|
|
@@ -1021,26 +1037,34 @@ export class IModelDb extends IModel {
|
|
|
1021
1037
|
this.cleanupSnapshot(context.resources);
|
|
1022
1038
|
}
|
|
1023
1039
|
}
|
|
1024
|
-
/**
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1040
|
+
/** Refuse a schema import this briefcase is in no state to run. Shared by [[importSchemas]] and
|
|
1041
|
+
* [[BriefcaseDb.upgradeSchemas]], which reach the native importer by different routes.
|
|
1042
|
+
* @internal
|
|
1043
|
+
*/
|
|
1044
|
+
assertCanImportSchemas() {
|
|
1045
|
+
if (!this.isBriefcaseDb())
|
|
1046
|
+
return;
|
|
1047
|
+
if (this.txns.rebaser.isRebasing) {
|
|
1048
|
+
throw new IModelError(IModelStatus.BadRequest, "Cannot import schemas while rebasing");
|
|
1049
|
+
}
|
|
1050
|
+
if (this.txns.isIndirectChanges) {
|
|
1051
|
+
throw new IModelError(IModelStatus.BadRequest, "Cannot import schemas while in an indirect change scope");
|
|
1052
|
+
}
|
|
1053
|
+
// Additional checks when semantic rebase is enabled
|
|
1054
|
+
if (IModelHost.useSemanticRebase) {
|
|
1055
|
+
if (this[_nativeDb].hasUnsavedChanges()) {
|
|
1056
|
+
throw new IModelError(IModelStatus.BadRequest, "Cannot import schemas with unsaved changes when useSemanticRebase flag is on");
|
|
1057
|
+
}
|
|
1058
|
+
if (SchemaSync.isEnabled(this)) {
|
|
1059
|
+
throw new IModelError(IModelStatus.BadRequest, "Cannot import schemas when schema sync is enabled and also useSemanticRebase flag is on");
|
|
1042
1060
|
}
|
|
1043
1061
|
}
|
|
1062
|
+
}
|
|
1063
|
+
/** Shared implementation for importing schemas from file or string.
|
|
1064
|
+
* @internal
|
|
1065
|
+
*/
|
|
1066
|
+
async importSchemasInternal(schemas, options, nativeImportOp) {
|
|
1067
|
+
this.assertCanImportSchemas();
|
|
1044
1068
|
if (options?.channelUpgrade) {
|
|
1045
1069
|
const channelUpgrade = options.channelUpgrade;
|
|
1046
1070
|
try {
|
|
@@ -1058,30 +1082,52 @@ export class IModelDb extends IModel {
|
|
|
1058
1082
|
if (options?.schemaImportCallbacks?.preSchemaImportCallback)
|
|
1059
1083
|
preSchemaImportCallbackResult = await this.preSchemaImportCallback(options.schemaImportCallbacks, { iModel: this, data: options.data, schemaData: schemas });
|
|
1060
1084
|
const maybeCustomNativeContext = options?.ecSchemaXmlContext?.nativeContext;
|
|
1061
|
-
if (
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
this.
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1085
|
+
if (SchemaSync.isEnabled(this)) {
|
|
1086
|
+
// The shared lock lets concurrent updates through while blocking anyone taking the exclusive lock for an upgrade.
|
|
1087
|
+
if (this[_nativeDb].getITwinId() !== Guid.empty)
|
|
1088
|
+
await this.locks.acquireLocks({ shared: IModel.repositoryModelId });
|
|
1089
|
+
// The native import adopts rows from the attached SchemaSyncDb. Attach and detach commit
|
|
1090
|
+
// implicitly, so abandoning changes cannot undo the adoption. A later failure must roll the
|
|
1091
|
+
// briefcase back before those ec_ rows can be pushed.
|
|
1092
|
+
let txnBeforeAdopt;
|
|
1093
|
+
// Set once the caller's own work is committed. Getting a token or the container write lock happens
|
|
1094
|
+
// before that, and abandonSchemaChanges discards every unsaved change rather than only this import's,
|
|
1095
|
+
// so abandoning on a failure from that window would throw away work the import never touched.
|
|
1096
|
+
let callerChangesSaved = false;
|
|
1097
|
+
try {
|
|
1098
|
+
await SchemaSync.withLockedAccess(this, { openMode: OpenMode.Readonly, operationName: "schema sync" }, async (syncAccess) => {
|
|
1099
|
+
const schemaSyncDbUri = syncAccess.getUri();
|
|
1100
|
+
this.saveSchemaChanges();
|
|
1101
|
+
callerChangesSaved = true;
|
|
1102
|
+
// After saveSchemaChanges, so rolling back cannot reach the caller's own work.
|
|
1103
|
+
if (this.isBriefcaseDb())
|
|
1104
|
+
txnBeforeAdopt = this.txns.getCurrentTxnId();
|
|
1105
|
+
try {
|
|
1106
|
+
nativeImportOp(schemas, { schemaLockHeld: false, ecSchemaXmlContext: maybeCustomNativeContext, schemaSyncDbUri });
|
|
1079
1107
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1108
|
+
catch (err) {
|
|
1109
|
+
if (SchemaSync.requiresUpgrade(err))
|
|
1110
|
+
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.`);
|
|
1111
|
+
throw new IModelError(err.errorNumber, err.message);
|
|
1082
1112
|
}
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
catch (err) {
|
|
1116
|
+
if (callerChangesSaved)
|
|
1117
|
+
this.abandonSchemaChanges();
|
|
1118
|
+
if (this.isBriefcaseDb() && undefined !== txnBeforeAdopt && this.txns.getCurrentTxnId() !== txnBeforeAdopt) {
|
|
1119
|
+
// Saving the adopt's txn started a new undo session, because schema changes are kept out of the
|
|
1120
|
+
// user's undo stack, so this reverses across that boundary. The changes themselves reverse like
|
|
1121
|
+
// any other - a rebase does it to every local txn on every pull. It does not reverse DDL: a table
|
|
1122
|
+
// or column the adopt created stays behind with no ec_ row describing it, which is the state a
|
|
1123
|
+
// briefcase already tolerates, and the next successful import finds the table up to date and
|
|
1124
|
+
// carries on. Cancel rather than reverse, so the rows cannot come back through undo either.
|
|
1125
|
+
const status = this[_nativeDb].cancelTo(txnBeforeAdopt, true);
|
|
1126
|
+
if (IModelStatus.Success !== status)
|
|
1127
|
+
Logger.logError(loggerCategory, `Failed to roll back the adopted schema rows after a failed import: ${IModelStatus[status] ?? status}`);
|
|
1083
1128
|
}
|
|
1084
|
-
|
|
1129
|
+
throw err;
|
|
1130
|
+
}
|
|
1085
1131
|
}
|
|
1086
1132
|
else {
|
|
1087
1133
|
const nativeImportOptions = {
|
|
@@ -2846,7 +2892,9 @@ function processSchemaWriteStatus(status) {
|
|
|
2846
2892
|
try {
|
|
2847
2893
|
props.push(imodel.elements.getElementProps(id));
|
|
2848
2894
|
}
|
|
2849
|
-
catch {
|
|
2895
|
+
catch {
|
|
2896
|
+
return;
|
|
2897
|
+
}
|
|
2850
2898
|
});
|
|
2851
2899
|
return props;
|
|
2852
2900
|
}
|
|
@@ -2869,7 +2917,9 @@ function processSchemaWriteStatus(status) {
|
|
|
2869
2917
|
break;
|
|
2870
2918
|
}
|
|
2871
2919
|
}
|
|
2872
|
-
catch {
|
|
2920
|
+
catch {
|
|
2921
|
+
continue;
|
|
2922
|
+
}
|
|
2873
2923
|
}
|
|
2874
2924
|
return finished;
|
|
2875
2925
|
}
|
|
@@ -3079,9 +3129,6 @@ export class BriefcaseDb extends IModelDb {
|
|
|
3079
3129
|
get isBriefcase() { return true; }
|
|
3080
3130
|
/* the BriefcaseId of the briefcase opened with this BriefcaseDb */
|
|
3081
3131
|
briefcaseId;
|
|
3082
|
-
_skipSyncSchemasOnPullAndPush;
|
|
3083
|
-
/** @internal */
|
|
3084
|
-
get skipSyncSchemasOnPullAndPush() { return this._skipSyncSchemasOnPullAndPush ?? false; }
|
|
3085
3132
|
/**
|
|
3086
3133
|
* Event raised just before a BriefcaseDb is opened. Supplies the arguments that will be used to open the BriefcaseDb.
|
|
3087
3134
|
* Throw an exception to stop the open.
|
|
@@ -3164,6 +3211,8 @@ export class BriefcaseDb extends IModelDb {
|
|
|
3164
3211
|
}
|
|
3165
3212
|
constructor(args) {
|
|
3166
3213
|
super({ ...args, changeset: args.nativeDb.getCurrentChangeset() });
|
|
3214
|
+
// Native invokes this private callback dynamically through the object registered by IModelDb.
|
|
3215
|
+
void this.onChangesetConflict;
|
|
3167
3216
|
this._openMode = args.openMode;
|
|
3168
3217
|
this.briefcaseId = args.briefcaseId;
|
|
3169
3218
|
this.makeLockControl();
|
|
@@ -3176,26 +3225,33 @@ export class BriefcaseDb extends IModelDb {
|
|
|
3176
3225
|
wasChanges = nativeDb.hasPendingTxns();
|
|
3177
3226
|
nativeDb.closeFile();
|
|
3178
3227
|
};
|
|
3179
|
-
const
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
}
|
|
3183
|
-
|
|
3228
|
+
const push = async () => {
|
|
3229
|
+
if (wasChanges)
|
|
3230
|
+
await withBriefcaseDb(briefcase, async (db) => db.pushChanges({ ...briefcase, description, retainLocks: true }));
|
|
3231
|
+
};
|
|
3232
|
+
// Asked of the closed file, so an iModel without schema sync never gets opened for this.
|
|
3233
|
+
if (SchemaSync.isEnabled(briefcase)) {
|
|
3234
|
+
await withBriefcaseDb(briefcase, async (db) => SchemaSync.updateDbSchema(db));
|
|
3184
3235
|
await SchemaSync.withLockedAccess(briefcase, { openMode: OpenMode.Readonly, operationName: "schema sync" }, async (syncAccess) => {
|
|
3185
3236
|
const schemaSyncDbUri = syncAccess.getUri();
|
|
3186
3237
|
executeUpgrade();
|
|
3238
|
+
// Overwriting advances the SchemaSync data version, so a no-op upgrade must not call it.
|
|
3239
|
+
if (!wasChanges)
|
|
3240
|
+
return;
|
|
3187
3241
|
await withBriefcaseDb(briefcase, async (db) => {
|
|
3188
|
-
db[_nativeDb].
|
|
3242
|
+
db[_nativeDb].schemaSyncOverwrite(schemaSyncDbUri);
|
|
3189
3243
|
db[_nativeDb].saveChanges();
|
|
3190
3244
|
});
|
|
3191
|
-
|
|
3245
|
+
// Publish the sync db before the changeset while retaining both locks.
|
|
3246
|
+
syncAccess.closeDb();
|
|
3247
|
+
await syncAccess.container.uploadChanges();
|
|
3248
|
+
await push();
|
|
3192
3249
|
});
|
|
3193
3250
|
}
|
|
3194
3251
|
else {
|
|
3195
3252
|
executeUpgrade();
|
|
3253
|
+
await push();
|
|
3196
3254
|
}
|
|
3197
|
-
if (wasChanges)
|
|
3198
|
-
await withBriefcaseDb(briefcase, async (db) => db.pushChanges({ ...briefcase, description, retainLocks: true }));
|
|
3199
3255
|
}
|
|
3200
3256
|
/** Upgrades the schemas in the iModel based on the current version of the software. Follows a sequence of operations -
|
|
3201
3257
|
* * Acquires a schema lock to prevent other users from making any other changes while upgrade is happening
|
|
@@ -3215,6 +3271,22 @@ export class BriefcaseDb extends IModelDb {
|
|
|
3215
3271
|
// - push changes
|
|
3216
3272
|
// - release schema lock
|
|
3217
3273
|
// 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.
|
|
3274
|
+
// schemaSyncOverwrite can discard unpushed imports, so hold the exclusive lock across both upgrades.
|
|
3275
|
+
if (SchemaSync.isEnabled(briefcase)) {
|
|
3276
|
+
try {
|
|
3277
|
+
await withBriefcaseDb(briefcase, async (db) => {
|
|
3278
|
+
if (!db.locks.isServerBased)
|
|
3279
|
+
throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cannot upgrade schemas with SchemaSync without server-based locking");
|
|
3280
|
+
await db.acquireSchemaLock();
|
|
3281
|
+
});
|
|
3282
|
+
await this.doUpgrade(briefcase, { profile: ProfileOptions.Upgrade, schemaLockHeld: true }, "Upgraded profile");
|
|
3283
|
+
await this.doUpgrade(briefcase, { domain: DomainOptions.Upgrade, schemaLockHeld: true }, "Upgraded domain schemas");
|
|
3284
|
+
}
|
|
3285
|
+
finally {
|
|
3286
|
+
await withBriefcaseDb(briefcase, async (db) => db.locks[_releaseAllLocks]());
|
|
3287
|
+
}
|
|
3288
|
+
return;
|
|
3289
|
+
}
|
|
3218
3290
|
try {
|
|
3219
3291
|
await this.doUpgrade(briefcase, { profile: ProfileOptions.Upgrade }, "Upgraded profile");
|
|
3220
3292
|
}
|
|
@@ -3335,6 +3407,13 @@ export class BriefcaseDb extends IModelDb {
|
|
|
3335
3407
|
return "foreign key";
|
|
3336
3408
|
}
|
|
3337
3409
|
};
|
|
3410
|
+
// sqlite_stat1 contains query-planner statistics, not iModel state. Its values can legitimately
|
|
3411
|
+
// differ between briefcases and are safe to replace with the incoming values.
|
|
3412
|
+
if (args.tableName === "sqlite_stat1" && (args.cause === DbConflictCause.Conflict || args.cause === DbConflictCause.Data)) {
|
|
3413
|
+
Logger.logWarning(category, `${interpretConflictCause(args.cause)} conflict on sqlite_stat1 - resolved by replacing the existing row with the incoming row`);
|
|
3414
|
+
args.dump();
|
|
3415
|
+
return DbConflictResolution.Replace;
|
|
3416
|
+
}
|
|
3338
3417
|
// `dgn_Domain` holds one bookkeeping row per BIS domain present in the briefcase. The row is
|
|
3339
3418
|
// created automatically as soon as the domain's schema is imported, so merging a changeset
|
|
3340
3419
|
// that registers a domain inserts a row that was just created locally. Both rows describe the
|
|
@@ -3515,8 +3594,7 @@ export class BriefcaseDb extends IModelDb {
|
|
|
3515
3594
|
async pullChanges(arg) {
|
|
3516
3595
|
await this.executeWritable(async () => {
|
|
3517
3596
|
await BriefcaseManager.pullAndApplyChangesets(this, arg ?? {});
|
|
3518
|
-
|
|
3519
|
-
await SchemaSync.pull(this);
|
|
3597
|
+
SchemaSync.updateDbSchema(this);
|
|
3520
3598
|
this.initializeIModelDb("pullMerge");
|
|
3521
3599
|
});
|
|
3522
3600
|
// If this pull enabled or disabled SchemaSync for this briefcase, its reservations must now be re-initialized
|
|
@@ -3601,24 +3679,10 @@ export class BriefcaseDb extends IModelDb {
|
|
|
3601
3679
|
if (nativeDb.hasPendingTxns()) {
|
|
3602
3680
|
throw new IModelError(ChangeSetStatus.HasLocalChanges, "Cannot revert with pending txns");
|
|
3603
3681
|
}
|
|
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
3682
|
this.clearCaches();
|
|
3619
|
-
await
|
|
3683
|
+
await this.pullChanges({ ...arg, toIndex: undefined });
|
|
3620
3684
|
await this.acquireSchemaLock();
|
|
3621
|
-
if (
|
|
3685
|
+
if (SchemaSync.isEnabled(this)) {
|
|
3622
3686
|
arg.skipSchemaChanges = true;
|
|
3623
3687
|
}
|
|
3624
3688
|
// The native side enables file-based txns during revert. Restore the original setting afterward.
|
|
@@ -3639,7 +3703,7 @@ export class BriefcaseDb extends IModelDb {
|
|
|
3639
3703
|
pushRetryDelay: arg.pushRetryDelay,
|
|
3640
3704
|
retainLocks: arg.retainLocks,
|
|
3641
3705
|
};
|
|
3642
|
-
await
|
|
3706
|
+
await this.pushChanges(pushArgs);
|
|
3643
3707
|
this.clearCaches();
|
|
3644
3708
|
}
|
|
3645
3709
|
catch (err) {
|
|
@@ -3716,6 +3780,56 @@ export class BriefcaseDb extends IModelDb {
|
|
|
3716
3780
|
this.txns._onChangesPushed(this.changeset);
|
|
3717
3781
|
BriefcaseManager.deleteRebaseFolders(this);
|
|
3718
3782
|
}
|
|
3783
|
+
/** Import schemas while allowing changes that move or delete data.
|
|
3784
|
+
*
|
|
3785
|
+
* Takes the exclusive schema lock, requires the briefcase to be at the timeline tip, imports, updates the sync db, and pushes the result.
|
|
3786
|
+
* @note The briefcase must have no local changes.
|
|
3787
|
+
* @see [[BriefcaseDb.upgradeSchemas]] (static) for upgrading the software's profile and domain schemas.
|
|
3788
|
+
* @alpha
|
|
3789
|
+
*/
|
|
3790
|
+
async upgradeSchemas(schemaFileNames, arg) {
|
|
3791
|
+
return this.upgradeSchemasInternal(schemaFileNames, arg, (schemas, importOptions) => this[_nativeDb].importSchemas(schemas, importOptions));
|
|
3792
|
+
}
|
|
3793
|
+
/** The [[importSchemaStrings]] counterpart of [[upgradeSchemas]].
|
|
3794
|
+
* @alpha
|
|
3795
|
+
*/
|
|
3796
|
+
async upgradeSchemaStrings(serializedXmlSchemas, arg) {
|
|
3797
|
+
return this.upgradeSchemasInternal(serializedXmlSchemas, arg, (schemas, importOptions) => this[_nativeDb].importXmlSchemas(schemas, importOptions));
|
|
3798
|
+
}
|
|
3799
|
+
async upgradeSchemasInternal(schemas, arg, nativeImportOp) {
|
|
3800
|
+
if (schemas.length === 0)
|
|
3801
|
+
return;
|
|
3802
|
+
this.assertCanImportSchemas();
|
|
3803
|
+
if (this[_nativeDb].hasUnsavedChanges() || this.txns.hasLocalChanges)
|
|
3804
|
+
throw new IModelError(ChangeSetStatus.HasLocalChanges, "Cannot upgrade schemas while there are local changes");
|
|
3805
|
+
if (SchemaSync.isEnabled(this) && !this.locks.isServerBased)
|
|
3806
|
+
throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cannot upgrade schemas with SchemaSync without server-based locking");
|
|
3807
|
+
await this.acquireSchemaLock();
|
|
3808
|
+
await this.pullChanges({ accessToken: arg.accessToken });
|
|
3809
|
+
if (!SchemaSync.isEnabled(this)) {
|
|
3810
|
+
await this.importSchemasInternal(schemas, { ecSchemaXmlContext: arg.ecSchemaXmlContext }, nativeImportOp);
|
|
3811
|
+
await this.pushChanges(arg);
|
|
3812
|
+
return;
|
|
3813
|
+
}
|
|
3814
|
+
await SchemaSync.withLockedAccess(this, { openMode: OpenMode.Readonly, operationName: "schema upgrade" }, async (syncAccess) => {
|
|
3815
|
+
this.saveSchemaChanges();
|
|
3816
|
+
try {
|
|
3817
|
+
nativeImportOp(schemas, {
|
|
3818
|
+
schemaLockHeld: true,
|
|
3819
|
+
ecSchemaXmlContext: arg.ecSchemaXmlContext?.nativeContext,
|
|
3820
|
+
schemaSyncDbUri: syncAccess.getUri(),
|
|
3821
|
+
});
|
|
3822
|
+
}
|
|
3823
|
+
catch (err) {
|
|
3824
|
+
this.abandonSchemaChanges();
|
|
3825
|
+
throw new IModelError(err.errorNumber, err.message);
|
|
3826
|
+
}
|
|
3827
|
+
this.clearCaches();
|
|
3828
|
+
syncAccess.closeDb();
|
|
3829
|
+
await syncAccess.container.uploadChanges();
|
|
3830
|
+
await this.pushChanges(arg);
|
|
3831
|
+
});
|
|
3832
|
+
}
|
|
3719
3833
|
close(options) {
|
|
3720
3834
|
if (this.isBriefcase && this.isOpen && !this.isReadonly && this.txns.rebaser.inProgress()) {
|
|
3721
3835
|
this[_nativeDb].abandonChanges();
|