@itwin/core-backend 4.2.0-dev.9 → 4.3.0-dev.0
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 +40 -1
- package/lib/cjs/BlobContainerService.d.ts +6 -2
- package/lib/cjs/BlobContainerService.d.ts.map +1 -1
- package/lib/cjs/BlobContainerService.js.map +1 -1
- package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
- package/lib/cjs/BriefcaseManager.js +5 -1
- package/lib/cjs/BriefcaseManager.js.map +1 -1
- package/lib/cjs/CheckpointManager.d.ts.map +1 -1
- package/lib/cjs/CheckpointManager.js +6 -3
- package/lib/cjs/CheckpointManager.js.map +1 -1
- package/lib/cjs/CloudSqlite.d.ts +79 -34
- package/lib/cjs/CloudSqlite.d.ts.map +1 -1
- package/lib/cjs/CloudSqlite.js +7 -9
- package/lib/cjs/CloudSqlite.js.map +1 -1
- package/lib/cjs/IModelDb.d.ts +23 -4
- package/lib/cjs/IModelDb.d.ts.map +1 -1
- package/lib/cjs/IModelDb.js +171 -42
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/cjs/SQLiteDb.js +1 -1
- package/lib/cjs/SQLiteDb.js.map +1 -1
- package/lib/cjs/SchemaSync.d.ts +41 -0
- package/lib/cjs/SchemaSync.d.ts.map +1 -0
- package/lib/cjs/SchemaSync.js +77 -0
- package/lib/cjs/SchemaSync.js.map +1 -0
- package/lib/cjs/TxnManager.d.ts +29 -0
- package/lib/cjs/TxnManager.d.ts.map +1 -1
- package/lib/cjs/TxnManager.js +20 -0
- package/lib/cjs/TxnManager.js.map +1 -1
- package/lib/cjs/core-backend.d.ts +1 -0
- package/lib/cjs/core-backend.d.ts.map +1 -1
- package/lib/cjs/core-backend.js +1 -0
- package/lib/cjs/core-backend.js.map +1 -1
- package/package.json +15 -12
package/lib/cjs/IModelDb.js
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.StandaloneDb = exports.SnapshotDb = exports.BriefcaseDb = exports.IModelDb = exports.BriefcaseLocalValue = void 0;
|
|
11
|
-
const path_1 = require("path");
|
|
12
11
|
const fs = require("fs");
|
|
12
|
+
const path_1 = require("path");
|
|
13
|
+
const touch = require("touch");
|
|
13
14
|
const core_bentley_1 = require("@itwin/core-bentley");
|
|
14
15
|
const core_common_1 = require("@itwin/core-common");
|
|
15
16
|
const core_geometry_1 = require("@itwin/core-geometry");
|
|
@@ -24,7 +25,6 @@ const CodeSpecs_1 = require("./CodeSpecs");
|
|
|
24
25
|
const ConcurrentQuery_1 = require("./ConcurrentQuery");
|
|
25
26
|
const ECSqlStatement_1 = require("./ECSqlStatement");
|
|
26
27
|
const Element_1 = require("./Element");
|
|
27
|
-
const ElementAspect_1 = require("./ElementAspect");
|
|
28
28
|
const ElementGraphics_1 = require("./ElementGraphics");
|
|
29
29
|
const Entity_1 = require("./Entity");
|
|
30
30
|
const GeoCoordConfig_1 = require("./GeoCoordConfig");
|
|
@@ -33,6 +33,7 @@ const IModelJsFs_1 = require("./IModelJsFs");
|
|
|
33
33
|
const IpcHost_1 = require("./IpcHost");
|
|
34
34
|
const Model_1 = require("./Model");
|
|
35
35
|
const Relationship_1 = require("./Relationship");
|
|
36
|
+
const SchemaSync_1 = require("./SchemaSync");
|
|
36
37
|
const ServerBasedLocks_1 = require("./ServerBasedLocks");
|
|
37
38
|
const SqliteStatement_1 = require("./SqliteStatement");
|
|
38
39
|
const TxnManager_1 = require("./TxnManager");
|
|
@@ -154,12 +155,18 @@ class IModelDb extends core_common_1.IModel {
|
|
|
154
155
|
(0, core_bentley_1.assert)(undefined !== super.iModelId);
|
|
155
156
|
return super.iModelId;
|
|
156
157
|
} // GuidString | undefined for the IModel superclass, but required for all IModelDb subclasses
|
|
157
|
-
/** @internal*/
|
|
158
|
-
get nativeDb() { return this._nativeDb; } // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
159
158
|
/** Get the full path fileName of this iModelDb
|
|
160
159
|
* @note this member is only valid while the iModel is opened.
|
|
161
160
|
*/
|
|
162
161
|
get pathName() { return this.nativeDb.getFilePath(); }
|
|
162
|
+
/** Get the full path to this iModel's "watch file".
|
|
163
|
+
* A read-only briefcase opened with `watchForChanges: true` creates this file next to the briefcase file on open, if it doesn't already exist.
|
|
164
|
+
* A writable briefcase "touches" this file if it exists whenever it commits changes to the briefcase.
|
|
165
|
+
* The read-only briefcase can use a file watcher to react when the writable briefcase makes changes to the briefcase.
|
|
166
|
+
* This is more reliable than watching the sqlite WAL file.
|
|
167
|
+
* @internal
|
|
168
|
+
*/
|
|
169
|
+
get watchFilePathName() { return `${this.pathName}-watch`; }
|
|
163
170
|
/** @internal */
|
|
164
171
|
constructor(args) {
|
|
165
172
|
super({ ...args, iTwinId: args.nativeDb.getITwinId(), iModelId: args.nativeDb.getIModelId() });
|
|
@@ -179,7 +186,7 @@ class IModelDb extends core_common_1.IModel {
|
|
|
179
186
|
this.onChangesetApplied = new core_bentley_1.BeEvent();
|
|
180
187
|
/** Event called when the iModel is about to be closed. */
|
|
181
188
|
this.onBeforeClose = new core_bentley_1.BeEvent();
|
|
182
|
-
this.
|
|
189
|
+
this.nativeDb = args.nativeDb;
|
|
183
190
|
this.nativeDb.setIModelDb(this);
|
|
184
191
|
this.loadSettingDictionaries();
|
|
185
192
|
GeoCoordConfig_1.GeoCoordConfig.loadForImodel(this.workspace.settings); // load gcs data specified by iModel's settings dictionaries, must be done before calling initializeIModelDb
|
|
@@ -209,7 +216,6 @@ class IModelDb extends core_common_1.IModel {
|
|
|
209
216
|
this._codeService?.close();
|
|
210
217
|
this._codeService = undefined;
|
|
211
218
|
this.nativeDb.closeIModel();
|
|
212
|
-
this._nativeDb = undefined; // the underlying nativeDb has been freed by closeIModel
|
|
213
219
|
}
|
|
214
220
|
/** @internal */
|
|
215
221
|
async refreshContainer(_userAccessToken) { }
|
|
@@ -260,7 +266,7 @@ class IModelDb extends core_common_1.IModel {
|
|
|
260
266
|
/** Return `true` if the underlying nativeDb is open and valid.
|
|
261
267
|
* @internal
|
|
262
268
|
*/
|
|
263
|
-
get isOpen() { return
|
|
269
|
+
get isOpen() { return this.nativeDb.isOpen(); }
|
|
264
270
|
/** Get the briefcase Id of this iModel */
|
|
265
271
|
getBriefcaseId() { return this.isOpen ? this.nativeDb.getBriefcaseId() : core_common_1.BriefcaseIdValue.Illegal; }
|
|
266
272
|
/**
|
|
@@ -336,9 +342,8 @@ class IModelDb extends core_common_1.IModel {
|
|
|
336
342
|
* @public
|
|
337
343
|
* */
|
|
338
344
|
createQueryReader(ecsql, params, config) {
|
|
339
|
-
if (!this.
|
|
345
|
+
if (!this.nativeDb.isOpen())
|
|
340
346
|
throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "db not open");
|
|
341
|
-
}
|
|
342
347
|
const executor = {
|
|
343
348
|
execute: async (request) => {
|
|
344
349
|
return ConcurrentQuery_1.ConcurrentQuery.executeQueryRequest(this.nativeDb, request);
|
|
@@ -541,6 +546,7 @@ class IModelDb extends core_common_1.IModel {
|
|
|
541
546
|
clearCaches() {
|
|
542
547
|
this._statementCache.clear();
|
|
543
548
|
this._sqliteStatementCache.clear();
|
|
549
|
+
this._classMetaDataRegistry = undefined;
|
|
544
550
|
}
|
|
545
551
|
/** Update the project extents for this iModel.
|
|
546
552
|
* <p><em>Example:</em>
|
|
@@ -630,16 +636,34 @@ class IModelDb extends core_common_1.IModel {
|
|
|
630
636
|
* @see querySchemaVersion
|
|
631
637
|
*/
|
|
632
638
|
async importSchemas(schemaFileNames, options) {
|
|
633
|
-
if (
|
|
634
|
-
|
|
639
|
+
if (schemaFileNames.length === 0)
|
|
640
|
+
return;
|
|
635
641
|
const maybeCustomNativeContext = options?.ecSchemaXmlContext?.nativeContext;
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
642
|
+
if (this.nativeDb.schemaSyncEnabled()) {
|
|
643
|
+
await SchemaSync_1.SchemaSync.withLockedAccess(this, { openMode: core_bentley_1.OpenMode.Readonly, operationName: "schema sync" }, async (syncAccess) => {
|
|
644
|
+
const schemaSyncDbUri = syncAccess.getUri();
|
|
645
|
+
this.saveChanges();
|
|
646
|
+
let stat = this.nativeDb.importSchemas(schemaFileNames, { schemaLockHeld: false, ecSchemaXmlContext: maybeCustomNativeContext, schemaSyncDbUri });
|
|
647
|
+
if (core_bentley_1.DbResult.BE_SQLITE_ERROR_SchemaLockFailed === stat) {
|
|
648
|
+
this.abandonChanges();
|
|
649
|
+
if (this.nativeDb.getITwinId() !== core_bentley_1.Guid.empty)
|
|
650
|
+
await this.acquireSchemaLock();
|
|
651
|
+
stat = this.nativeDb.importSchemas(schemaFileNames, { schemaLockHeld: true, ecSchemaXmlContext: maybeCustomNativeContext, schemaSyncDbUri });
|
|
652
|
+
}
|
|
653
|
+
if (core_bentley_1.DbResult.BE_SQLITE_OK !== stat)
|
|
654
|
+
throw new core_common_1.IModelError(stat, "Error importing schema");
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
const nativeImportOptions = {
|
|
659
|
+
schemaLockHeld: true,
|
|
660
|
+
ecSchemaXmlContext: maybeCustomNativeContext,
|
|
661
|
+
};
|
|
662
|
+
if (this.nativeDb.getITwinId() !== core_bentley_1.Guid.empty) // if this iModel is associated with an iTwin, importing schema requires the schema lock
|
|
663
|
+
await this.acquireSchemaLock();
|
|
664
|
+
const stat = this.nativeDb.importSchemas(schemaFileNames, nativeImportOptions);
|
|
665
|
+
if (core_bentley_1.DbResult.BE_SQLITE_OK !== stat)
|
|
666
|
+
throw new core_common_1.IModelError(stat, "Error importing schema");
|
|
643
667
|
}
|
|
644
668
|
this.clearCaches();
|
|
645
669
|
}
|
|
@@ -653,11 +677,30 @@ class IModelDb extends core_common_1.IModel {
|
|
|
653
677
|
* @alpha
|
|
654
678
|
*/
|
|
655
679
|
async importSchemaStrings(serializedXmlSchemas) {
|
|
656
|
-
if (
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
680
|
+
if (serializedXmlSchemas.length === 0)
|
|
681
|
+
return;
|
|
682
|
+
if (this.nativeDb.schemaSyncEnabled()) {
|
|
683
|
+
await SchemaSync_1.SchemaSync.withLockedAccess(this, { openMode: core_bentley_1.OpenMode.Readonly, operationName: "schemaSync" }, async (syncAccess) => {
|
|
684
|
+
const schemaSyncDbUri = syncAccess.getUri();
|
|
685
|
+
this.saveChanges();
|
|
686
|
+
let stat = this.nativeDb.importXmlSchemas(serializedXmlSchemas, { schemaLockHeld: false, schemaSyncDbUri });
|
|
687
|
+
if (core_bentley_1.DbResult.BE_SQLITE_ERROR_SchemaLockFailed === stat) {
|
|
688
|
+
this.abandonChanges();
|
|
689
|
+
if (this.nativeDb.getITwinId() !== core_bentley_1.Guid.empty)
|
|
690
|
+
await this.acquireSchemaLock();
|
|
691
|
+
stat = this.nativeDb.importXmlSchemas(serializedXmlSchemas, { schemaLockHeld: true, schemaSyncDbUri });
|
|
692
|
+
}
|
|
693
|
+
if (core_bentley_1.DbResult.BE_SQLITE_OK !== stat)
|
|
694
|
+
throw new core_common_1.IModelError(stat, "Error importing schema");
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
else {
|
|
698
|
+
if (this.iTwinId && this.iTwinId !== core_bentley_1.Guid.empty) // if this iModel is associated with an iTwin, importing schema requires the schema lock
|
|
699
|
+
await this.acquireSchemaLock();
|
|
700
|
+
const stat = this.nativeDb.importXmlSchemas(serializedXmlSchemas, { schemaLockHeld: true });
|
|
701
|
+
if (core_bentley_1.DbResult.BE_SQLITE_OK !== stat)
|
|
702
|
+
throw new core_common_1.IModelError(stat, "Error importing schema");
|
|
703
|
+
}
|
|
661
704
|
this.clearCaches();
|
|
662
705
|
}
|
|
663
706
|
/** Find an opened instance of any subclass of IModelDb, by filename
|
|
@@ -1706,18 +1749,77 @@ exports.IModelDb = IModelDb;
|
|
|
1706
1749
|
}
|
|
1707
1750
|
return this._queryAspect(aspectInstanceId, aspectClassFullName);
|
|
1708
1751
|
}
|
|
1752
|
+
runInstanceQuery(sql, elementId, excludedClassFullNames) {
|
|
1753
|
+
return this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1754
|
+
statement.bindId("elementId", elementId);
|
|
1755
|
+
const aspects = [];
|
|
1756
|
+
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1757
|
+
const row = {};
|
|
1758
|
+
const parsedRow = JSON.parse(statement.getValue(0).getString());
|
|
1759
|
+
// eslint-disable-next-line guard-for-in
|
|
1760
|
+
for (const key in parsedRow) {
|
|
1761
|
+
const jsName = core_common_1.ECJsNames.toJsName(key[0].toUpperCase() + key.substring(1));
|
|
1762
|
+
Object.defineProperty(row, jsName, { enumerable: true, configurable: true, writable: true, value: parsedRow[key] });
|
|
1763
|
+
}
|
|
1764
|
+
const aspectProps = row;
|
|
1765
|
+
aspectProps.classFullName = aspectProps.className.replace(".", ":"); // add in property required by EntityProps
|
|
1766
|
+
aspectProps.className = undefined; // clear property from SELECT $ that we don't want in the final instance
|
|
1767
|
+
if ((undefined === excludedClassFullNames) || !excludedClassFullNames.has(aspectProps.classFullName))
|
|
1768
|
+
aspects.push(this._iModel.constructEntity(aspectProps));
|
|
1769
|
+
}
|
|
1770
|
+
return aspects;
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1709
1773
|
/** Get the ElementAspect instances that are owned by the specified element.
|
|
1710
1774
|
* @param elementId Get ElementAspects associated with this Element
|
|
1711
1775
|
* @param aspectClassFullName Optionally filter ElementAspects polymorphically by this class name
|
|
1776
|
+
* @param excludedClassFullNames Optional filter to exclude aspects from classes in the given set.
|
|
1712
1777
|
* @throws [[IModelError]]
|
|
1713
1778
|
*/
|
|
1714
|
-
getAspects(elementId, aspectClassFullName) {
|
|
1715
|
-
if (
|
|
1716
|
-
const
|
|
1717
|
-
|
|
1718
|
-
|
|
1779
|
+
getAspects(elementId, aspectClassFullName, excludedClassFullNames) {
|
|
1780
|
+
if (aspectClassFullName === undefined) {
|
|
1781
|
+
const allAspects = this.runInstanceQuery(`SELECT $ FROM (
|
|
1782
|
+
SELECT ECInstanceId, ECClassId FROM Bis.ElementMultiAspect WHERE Element.Id = :elementId
|
|
1783
|
+
UNION ALL
|
|
1784
|
+
SELECT ECInstanceId, ECClassId FROM Bis.ElementUniqueAspect WHERE Element.Id = :elementId) OPTIONS USE_JS_PROP_NAMES DO_NOT_TRUNCATE_BLOB`, elementId, excludedClassFullNames);
|
|
1785
|
+
if (allAspects.length === 0)
|
|
1786
|
+
core_bentley_1.Logger.logError(BackendLoggerCategory_1.BackendLoggerCategory.ECDb, `No aspects found for class ${aspectClassFullName} and element ${elementId}`);
|
|
1787
|
+
return allAspects;
|
|
1788
|
+
}
|
|
1789
|
+
// Check if class is abstract
|
|
1790
|
+
const fullClassName = aspectClassFullName.split(":");
|
|
1791
|
+
const val = this._iModel.nativeDb.getECClassMetaData(fullClassName[0], fullClassName[1]);
|
|
1792
|
+
if (val.result !== undefined) {
|
|
1793
|
+
const metaData = new core_common_1.EntityMetaData(JSON.parse(val.result));
|
|
1794
|
+
if (metaData.modifier !== "Abstract") // Class is not abstract, use normal query to retrieve aspects
|
|
1795
|
+
return this._queryAspects(elementId, aspectClassFullName, excludedClassFullNames);
|
|
1719
1796
|
}
|
|
1720
|
-
|
|
1797
|
+
// If class specified is abstract, get the list of all classes derived from it
|
|
1798
|
+
let classIdList = IModelDb.Elements.classMap.get(aspectClassFullName);
|
|
1799
|
+
if (classIdList === undefined) {
|
|
1800
|
+
const classIds = [];
|
|
1801
|
+
this._iModel.withPreparedStatement(`select SourceECInstanceId from meta.ClassHasAllBaseClasses where TargetECInstanceId = (select ECInstanceId from meta.ECClassDef where Name='${fullClassName[1]}'
|
|
1802
|
+
and Schema.Id = (select ECInstanceId from meta.ECSchemaDef where Name='${fullClassName[0]}')) and SourceECInstanceId != TargetECInstanceId`, (statement) => {
|
|
1803
|
+
while (statement.step() === core_bentley_1.DbResult.BE_SQLITE_ROW)
|
|
1804
|
+
classIds.push(statement.getValue(0).getId());
|
|
1805
|
+
});
|
|
1806
|
+
if (classIds.length > 0) {
|
|
1807
|
+
classIdList = classIds.join(",");
|
|
1808
|
+
IModelDb.Elements.classMap.set(aspectClassFullName, classIdList);
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
if (classIdList === undefined) {
|
|
1812
|
+
core_bentley_1.Logger.logError(BackendLoggerCategory_1.BackendLoggerCategory.ECDb, `No aspects found for the class ${aspectClassFullName}`);
|
|
1813
|
+
return [];
|
|
1814
|
+
}
|
|
1815
|
+
// Execute an instance query to retrieve all aspects from all the derived classes
|
|
1816
|
+
const aspects = this.runInstanceQuery(`SELECT $ FROM (
|
|
1817
|
+
SELECT ECInstanceId, ECClassId FROM Bis.ElementMultiAspect WHERE Element.Id = :elementId AND ECClassId IN (${classIdList})
|
|
1818
|
+
UNION ALL
|
|
1819
|
+
SELECT ECInstanceId, ECClassId FROM Bis.ElementUniqueAspect WHERE Element.Id = :elementId AND ECClassId IN (${classIdList})
|
|
1820
|
+
) OPTIONS USE_JS_PROP_NAMES DO_NOT_TRUNCATE_BLOB`, elementId, excludedClassFullNames);
|
|
1821
|
+
if (aspects.length === 0)
|
|
1822
|
+
core_bentley_1.Logger.logError(BackendLoggerCategory_1.BackendLoggerCategory.ECDb, `No aspects found for class ${aspectClassFullName} and element ${elementId}`);
|
|
1721
1823
|
return aspects;
|
|
1722
1824
|
}
|
|
1723
1825
|
/** Insert a new ElementAspect into the iModel.
|
|
@@ -1763,6 +1865,7 @@ exports.IModelDb = IModelDb;
|
|
|
1763
1865
|
});
|
|
1764
1866
|
}
|
|
1765
1867
|
}
|
|
1868
|
+
Elements.classMap = new Map();
|
|
1766
1869
|
IModelDb.Elements = Elements;
|
|
1767
1870
|
/** The collection of views in an [[IModelDb]].
|
|
1768
1871
|
* @public
|
|
@@ -1942,8 +2045,10 @@ exports.IModelDb = IModelDb;
|
|
|
1942
2045
|
this._iModel.nativeDb.saveFileProperty(viewArg, JSON.stringify(props), thumbnail.image);
|
|
1943
2046
|
return 0;
|
|
1944
2047
|
}
|
|
1945
|
-
/** Set the default view property the iModel
|
|
2048
|
+
/** Set the default view property the iModel.
|
|
1946
2049
|
* @param viewId The Id of the ViewDefinition to use as the default
|
|
2050
|
+
* @deprecated in 4.2.x. Avoid setting this property - it is not practical for one single view to serve the needs of the many applications
|
|
2051
|
+
* that might wish to view the contents of the iModel.
|
|
1947
2052
|
*/
|
|
1948
2053
|
setDefaultViewId(viewId) {
|
|
1949
2054
|
const spec = { namespace: "dgn_View", name: "DefaultView" };
|
|
@@ -2124,12 +2229,20 @@ class BriefcaseDb extends IModelDb {
|
|
|
2124
2229
|
const openMode = (args.readonly || args.watchForChanges) ? core_bentley_1.OpenMode.Readonly : core_bentley_1.OpenMode.ReadWrite;
|
|
2125
2230
|
const nativeDb = this.openDgnDb(file, openMode, undefined, args);
|
|
2126
2231
|
const briefcaseDb = new BriefcaseDb({ nativeDb, key: file.key ?? core_bentley_1.Guid.createValue(), openMode, briefcaseId: nativeDb.getBriefcaseId() });
|
|
2127
|
-
// If they asked to watch for changes, set an fs.watch on the "-
|
|
2232
|
+
// If they asked to watch for changes, set an fs.watch on the "-watch" file (only it is modified while we hold this connection.)
|
|
2128
2233
|
// Whenever there are changes, restart our defaultTxn. That loads the changes from the other connection and sends
|
|
2129
2234
|
// notifications as if they happened on this connection. Note: the watcher is called only when the backend event loop cycles.
|
|
2130
2235
|
if (args.watchForChanges && undefined === args.container) {
|
|
2131
|
-
|
|
2132
|
-
|
|
2236
|
+
// Must touch the file synchronously - cannot watch a file until it exists.
|
|
2237
|
+
touch.sync(briefcaseDb.watchFilePathName);
|
|
2238
|
+
// Restart default txn to trigger events when watch file is changed by some other process.
|
|
2239
|
+
const watcher = fs.watch(briefcaseDb.watchFilePathName, { persistent: false }, () => {
|
|
2240
|
+
nativeDb.restartDefaultTxn();
|
|
2241
|
+
});
|
|
2242
|
+
// Stop the watcher when we close this connection.
|
|
2243
|
+
briefcaseDb.onBeforeClose.addOnce(() => {
|
|
2244
|
+
watcher.close();
|
|
2245
|
+
});
|
|
2133
2246
|
}
|
|
2134
2247
|
if (openMode === core_bentley_1.OpenMode.ReadWrite && CodeService_1.CodeService.createForIModel) {
|
|
2135
2248
|
try {
|
|
@@ -2144,23 +2257,39 @@ class BriefcaseDb extends IModelDb {
|
|
|
2144
2257
|
this.onOpened.raiseEvent(briefcaseDb, args);
|
|
2145
2258
|
return briefcaseDb;
|
|
2146
2259
|
}
|
|
2147
|
-
|
|
2260
|
+
/** If the briefcase is read-only, reopen the native briefcase for writing.
|
|
2261
|
+
* Execute the supplied function.
|
|
2262
|
+
* If the briefcase was read-only, reopen the native briefcase as read-only.
|
|
2263
|
+
* @note this._openMode is not changed from its initial value.
|
|
2264
|
+
* @internal Exported strictly for tests.
|
|
2265
|
+
*/
|
|
2266
|
+
async executeWritable(func) {
|
|
2148
2267
|
const fileName = this.pathName;
|
|
2268
|
+
try {
|
|
2269
|
+
if (this.isReadonly)
|
|
2270
|
+
this.closeAndReopen(core_bentley_1.OpenMode.ReadWrite, fileName);
|
|
2271
|
+
await func();
|
|
2272
|
+
}
|
|
2273
|
+
finally {
|
|
2274
|
+
if (this.isReadonly)
|
|
2275
|
+
this.closeAndReopen(core_bentley_1.OpenMode.Readonly, fileName);
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
closeAndReopen(openMode, fileName) {
|
|
2279
|
+
// Unclosed statements will produce BUSY error when attempting to close.
|
|
2280
|
+
this.clearCaches();
|
|
2281
|
+
// The following resets the native db's pointer to this JavaScript object.
|
|
2149
2282
|
this.nativeDb.closeIModel();
|
|
2150
2283
|
this.nativeDb.openIModel(fileName, openMode);
|
|
2284
|
+
// Restore the native db's pointer to this JavaScript object.
|
|
2285
|
+
this.nativeDb.setIModelDb(this);
|
|
2151
2286
|
}
|
|
2152
2287
|
/** Pull and apply changesets from iModelHub */
|
|
2153
2288
|
async pullChanges(arg) {
|
|
2154
|
-
|
|
2155
|
-
this.closeAndReopen(core_bentley_1.OpenMode.ReadWrite);
|
|
2156
|
-
try {
|
|
2289
|
+
await this.executeWritable(async () => {
|
|
2157
2290
|
await BriefcaseManager_1.BriefcaseManager.pullAndApplyChangesets(this, arg ?? {});
|
|
2158
2291
|
this.initializeIModelDb();
|
|
2159
|
-
}
|
|
2160
|
-
finally {
|
|
2161
|
-
if (this.isReadonly) // if the briefcase was opened readonly - close and reopen it readonly
|
|
2162
|
-
this.closeAndReopen(core_bentley_1.OpenMode.Readonly);
|
|
2163
|
-
}
|
|
2292
|
+
});
|
|
2164
2293
|
IpcHost_1.IpcHost.notifyTxns(this, "notifyPulledChanges", this.changeset);
|
|
2165
2294
|
}
|
|
2166
2295
|
/** Push changes to iModelHub. */
|