@itwin/core-backend 4.2.0-dev.3 → 4.2.0-dev.31
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 +88 -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 +11 -9
- package/lib/cjs/CloudSqlite.js.map +1 -1
- package/lib/cjs/IModelDb.d.ts +40 -7
- package/lib/cjs/IModelDb.d.ts.map +1 -1
- package/lib/cjs/IModelDb.js +196 -45
- 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/SchemaUtils.d.ts.map +1 -1
- package/lib/cjs/SchemaUtils.js +3 -3
- package/lib/cjs/SchemaUtils.js.map +1 -1
- package/lib/cjs/TileStorage.d.ts +16 -5
- package/lib/cjs/TileStorage.d.ts.map +1 -1
- package/lib/cjs/TileStorage.js +34 -3
- package/lib/cjs/TileStorage.js.map +1 -1
- package/lib/cjs/TxnManager.d.ts +41 -0
- package/lib/cjs/TxnManager.d.ts.map +1 -1
- package/lib/cjs/TxnManager.js +38 -0
- package/lib/cjs/TxnManager.js.map +1 -1
- package/lib/cjs/ViewStore.js +1 -1
- package/lib/cjs/ViewStore.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/lib/cjs/rpc-impl/RpcBriefcaseUtility.js +2 -2
- package/lib/cjs/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
- package/lib/cjs/workspace/Workspace.d.ts +2 -4
- package/lib/cjs/workspace/Workspace.d.ts.map +1 -1
- package/lib/cjs/workspace/Workspace.js +4 -6
- package/lib/cjs/workspace/Workspace.js.map +1 -1
- package/package.json +18 -15
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");
|
|
@@ -123,6 +124,10 @@ class IModelDb extends core_common_1.IModel {
|
|
|
123
124
|
this.changeset = this.nativeDb.getCurrentChangeset();
|
|
124
125
|
this.onChangesetApplied.raiseEvent();
|
|
125
126
|
}
|
|
127
|
+
/** @internal */
|
|
128
|
+
restartDefaultTxn() {
|
|
129
|
+
this.nativeDb.restartDefaultTxn();
|
|
130
|
+
}
|
|
126
131
|
get fontMap() {
|
|
127
132
|
return this._fontMap ?? (this._fontMap = new core_common_1.FontMap(this.nativeDb.readFontMap()));
|
|
128
133
|
}
|
|
@@ -150,12 +155,18 @@ class IModelDb extends core_common_1.IModel {
|
|
|
150
155
|
(0, core_bentley_1.assert)(undefined !== super.iModelId);
|
|
151
156
|
return super.iModelId;
|
|
152
157
|
} // GuidString | undefined for the IModel superclass, but required for all IModelDb subclasses
|
|
153
|
-
/** @internal*/
|
|
154
|
-
get nativeDb() { return this._nativeDb; } // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
155
158
|
/** Get the full path fileName of this iModelDb
|
|
156
159
|
* @note this member is only valid while the iModel is opened.
|
|
157
160
|
*/
|
|
158
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`; }
|
|
159
170
|
/** @internal */
|
|
160
171
|
constructor(args) {
|
|
161
172
|
super({ ...args, iTwinId: args.nativeDb.getITwinId(), iModelId: args.nativeDb.getIModelId() });
|
|
@@ -175,7 +186,7 @@ class IModelDb extends core_common_1.IModel {
|
|
|
175
186
|
this.onChangesetApplied = new core_bentley_1.BeEvent();
|
|
176
187
|
/** Event called when the iModel is about to be closed. */
|
|
177
188
|
this.onBeforeClose = new core_bentley_1.BeEvent();
|
|
178
|
-
this.
|
|
189
|
+
this.nativeDb = args.nativeDb;
|
|
179
190
|
this.nativeDb.setIModelDb(this);
|
|
180
191
|
this.loadSettingDictionaries();
|
|
181
192
|
GeoCoordConfig_1.GeoCoordConfig.loadForImodel(this.workspace.settings); // load gcs data specified by iModel's settings dictionaries, must be done before calling initializeIModelDb
|
|
@@ -205,10 +216,9 @@ class IModelDb extends core_common_1.IModel {
|
|
|
205
216
|
this._codeService?.close();
|
|
206
217
|
this._codeService = undefined;
|
|
207
218
|
this.nativeDb.closeIModel();
|
|
208
|
-
this._nativeDb = undefined; // the underlying nativeDb has been freed by closeIModel
|
|
209
219
|
}
|
|
210
220
|
/** @internal */
|
|
211
|
-
async
|
|
221
|
+
async refreshContainer(_userAccessToken) { }
|
|
212
222
|
/**
|
|
213
223
|
* Called by derived classes before closing the connection
|
|
214
224
|
* @internal
|
|
@@ -256,7 +266,7 @@ class IModelDb extends core_common_1.IModel {
|
|
|
256
266
|
/** Return `true` if the underlying nativeDb is open and valid.
|
|
257
267
|
* @internal
|
|
258
268
|
*/
|
|
259
|
-
get isOpen() { return
|
|
269
|
+
get isOpen() { return this.nativeDb.isOpen(); }
|
|
260
270
|
/** Get the briefcase Id of this iModel */
|
|
261
271
|
getBriefcaseId() { return this.isOpen ? this.nativeDb.getBriefcaseId() : core_common_1.BriefcaseIdValue.Illegal; }
|
|
262
272
|
/**
|
|
@@ -332,9 +342,8 @@ class IModelDb extends core_common_1.IModel {
|
|
|
332
342
|
* @public
|
|
333
343
|
* */
|
|
334
344
|
createQueryReader(ecsql, params, config) {
|
|
335
|
-
if (!this.
|
|
345
|
+
if (!this.nativeDb.isOpen())
|
|
336
346
|
throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "db not open");
|
|
337
|
-
}
|
|
338
347
|
const executor = {
|
|
339
348
|
execute: async (request) => {
|
|
340
349
|
return ConcurrentQuery_1.ConcurrentQuery.executeQueryRequest(this.nativeDb, request);
|
|
@@ -537,6 +546,7 @@ class IModelDb extends core_common_1.IModel {
|
|
|
537
546
|
clearCaches() {
|
|
538
547
|
this._statementCache.clear();
|
|
539
548
|
this._sqliteStatementCache.clear();
|
|
549
|
+
this._classMetaDataRegistry = undefined;
|
|
540
550
|
}
|
|
541
551
|
/** Update the project extents for this iModel.
|
|
542
552
|
* <p><em>Example:</em>
|
|
@@ -626,16 +636,34 @@ class IModelDb extends core_common_1.IModel {
|
|
|
626
636
|
* @see querySchemaVersion
|
|
627
637
|
*/
|
|
628
638
|
async importSchemas(schemaFileNames, options) {
|
|
629
|
-
if (
|
|
630
|
-
|
|
639
|
+
if (schemaFileNames.length === 0)
|
|
640
|
+
return;
|
|
631
641
|
const maybeCustomNativeContext = options?.ecSchemaXmlContext?.nativeContext;
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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");
|
|
639
667
|
}
|
|
640
668
|
this.clearCaches();
|
|
641
669
|
}
|
|
@@ -649,11 +677,30 @@ class IModelDb extends core_common_1.IModel {
|
|
|
649
677
|
* @alpha
|
|
650
678
|
*/
|
|
651
679
|
async importSchemaStrings(serializedXmlSchemas) {
|
|
652
|
-
if (
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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
|
+
}
|
|
657
704
|
this.clearCaches();
|
|
658
705
|
}
|
|
659
706
|
/** Find an opened instance of any subclass of IModelDb, by filename
|
|
@@ -1094,6 +1141,16 @@ class IModelDb extends core_common_1.IModel {
|
|
|
1094
1141
|
}
|
|
1095
1142
|
});
|
|
1096
1143
|
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Controls how [Code]($common)s are copied from this iModel into another iModel, to work around problems with iModels created by older connectors. The [imodel-transformer](https://github.com/iTwin/imodel-transformer) sets this appropriately on your behalf - you should never need to set or interrogate this property yourself.
|
|
1146
|
+
* @public
|
|
1147
|
+
*/
|
|
1148
|
+
get codeValueBehavior() {
|
|
1149
|
+
return this.nativeDb.getCodeValueBehavior();
|
|
1150
|
+
}
|
|
1151
|
+
set codeValueBehavior(newBehavior) {
|
|
1152
|
+
this.nativeDb.setCodeValueBehavior(newBehavior);
|
|
1153
|
+
}
|
|
1097
1154
|
}
|
|
1098
1155
|
/** Keep track of open imodels to support `tryFind` for RPC purposes */
|
|
1099
1156
|
IModelDb._openDbs = new Map();
|
|
@@ -1692,18 +1749,77 @@ exports.IModelDb = IModelDb;
|
|
|
1692
1749
|
}
|
|
1693
1750
|
return this._queryAspect(aspectInstanceId, aspectClassFullName);
|
|
1694
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
|
+
}
|
|
1695
1773
|
/** Get the ElementAspect instances that are owned by the specified element.
|
|
1696
1774
|
* @param elementId Get ElementAspects associated with this Element
|
|
1697
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.
|
|
1698
1777
|
* @throws [[IModelError]]
|
|
1699
1778
|
*/
|
|
1700
|
-
getAspects(elementId, aspectClassFullName) {
|
|
1701
|
-
if (
|
|
1702
|
-
const
|
|
1703
|
-
|
|
1704
|
-
|
|
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;
|
|
1705
1788
|
}
|
|
1706
|
-
|
|
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);
|
|
1796
|
+
}
|
|
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}`);
|
|
1707
1823
|
return aspects;
|
|
1708
1824
|
}
|
|
1709
1825
|
/** Insert a new ElementAspect into the iModel.
|
|
@@ -1749,6 +1865,7 @@ exports.IModelDb = IModelDb;
|
|
|
1749
1865
|
});
|
|
1750
1866
|
}
|
|
1751
1867
|
}
|
|
1868
|
+
Elements.classMap = new Map();
|
|
1752
1869
|
IModelDb.Elements = Elements;
|
|
1753
1870
|
/** The collection of views in an [[IModelDb]].
|
|
1754
1871
|
* @public
|
|
@@ -1928,8 +2045,10 @@ exports.IModelDb = IModelDb;
|
|
|
1928
2045
|
this._iModel.nativeDb.saveFileProperty(viewArg, JSON.stringify(props), thumbnail.image);
|
|
1929
2046
|
return 0;
|
|
1930
2047
|
}
|
|
1931
|
-
/** Set the default view property the iModel
|
|
2048
|
+
/** Set the default view property the iModel.
|
|
1932
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.
|
|
1933
2052
|
*/
|
|
1934
2053
|
setDefaultViewId(viewId) {
|
|
1935
2054
|
const spec = { namespace: "dgn_View", name: "DefaultView" };
|
|
@@ -2110,12 +2229,20 @@ class BriefcaseDb extends IModelDb {
|
|
|
2110
2229
|
const openMode = (args.readonly || args.watchForChanges) ? core_bentley_1.OpenMode.Readonly : core_bentley_1.OpenMode.ReadWrite;
|
|
2111
2230
|
const nativeDb = this.openDgnDb(file, openMode, undefined, args);
|
|
2112
2231
|
const briefcaseDb = new BriefcaseDb({ nativeDb, key: file.key ?? core_bentley_1.Guid.createValue(), openMode, briefcaseId: nativeDb.getBriefcaseId() });
|
|
2113
|
-
// 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.)
|
|
2114
2233
|
// Whenever there are changes, restart our defaultTxn. That loads the changes from the other connection and sends
|
|
2115
2234
|
// notifications as if they happened on this connection. Note: the watcher is called only when the backend event loop cycles.
|
|
2116
2235
|
if (args.watchForChanges && undefined === args.container) {
|
|
2117
|
-
|
|
2118
|
-
|
|
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
|
+
});
|
|
2119
2246
|
}
|
|
2120
2247
|
if (openMode === core_bentley_1.OpenMode.ReadWrite && CodeService_1.CodeService.createForIModel) {
|
|
2121
2248
|
try {
|
|
@@ -2130,23 +2257,39 @@ class BriefcaseDb extends IModelDb {
|
|
|
2130
2257
|
this.onOpened.raiseEvent(briefcaseDb, args);
|
|
2131
2258
|
return briefcaseDb;
|
|
2132
2259
|
}
|
|
2133
|
-
|
|
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) {
|
|
2134
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.
|
|
2135
2282
|
this.nativeDb.closeIModel();
|
|
2136
2283
|
this.nativeDb.openIModel(fileName, openMode);
|
|
2284
|
+
// Restore the native db's pointer to this JavaScript object.
|
|
2285
|
+
this.nativeDb.setIModelDb(this);
|
|
2137
2286
|
}
|
|
2138
2287
|
/** Pull and apply changesets from iModelHub */
|
|
2139
2288
|
async pullChanges(arg) {
|
|
2140
|
-
|
|
2141
|
-
this.closeAndReopen(core_bentley_1.OpenMode.ReadWrite);
|
|
2142
|
-
try {
|
|
2289
|
+
await this.executeWritable(async () => {
|
|
2143
2290
|
await BriefcaseManager_1.BriefcaseManager.pullAndApplyChangesets(this, arg ?? {});
|
|
2144
2291
|
this.initializeIModelDb();
|
|
2145
|
-
}
|
|
2146
|
-
finally {
|
|
2147
|
-
if (this.isReadonly) // if the briefcase was opened readonly - close and reopen it readonly
|
|
2148
|
-
this.closeAndReopen(core_bentley_1.OpenMode.Readonly);
|
|
2149
|
-
}
|
|
2292
|
+
});
|
|
2150
2293
|
IpcHost_1.IpcHost.notifyTxns(this, "notifyPulledChanges", this.changeset);
|
|
2151
2294
|
}
|
|
2152
2295
|
/** Push changes to iModelHub. */
|
|
@@ -2354,18 +2497,26 @@ class SnapshotDb extends IModelDb {
|
|
|
2354
2497
|
snapshot.close();
|
|
2355
2498
|
throw err;
|
|
2356
2499
|
}
|
|
2500
|
+
// unref timer, so it doesn't prevent a process from shutting down.
|
|
2501
|
+
snapshot._restartDefaultTxnTimer = setTimeout(() => {
|
|
2502
|
+
snapshot.restartDefaultTxn();
|
|
2503
|
+
}, (10 * 60) * 1000).unref(); // 10 * 60 is 10 minutes in seconds, then converted to milliseconds (* 1000);
|
|
2357
2504
|
snapshot._refreshSas = new RefreshV2CheckpointSas(container.accessToken, checkpoint.reattachSafetySeconds);
|
|
2358
2505
|
return snapshot;
|
|
2359
2506
|
}
|
|
2360
|
-
/** Used to refresh the container sasToken using the current user's accessToken
|
|
2507
|
+
/** Used to refresh the container sasToken using the current user's accessToken.
|
|
2508
|
+
* Also restarts the timer which causes the default txn to be restarted on db if the timer activates.
|
|
2361
2509
|
* @internal
|
|
2362
2510
|
*/
|
|
2363
|
-
async
|
|
2511
|
+
async refreshContainer(userAccessToken) {
|
|
2512
|
+
this._restartDefaultTxnTimer?.refresh();
|
|
2364
2513
|
return this._refreshSas?.refreshSas(userAccessToken, this);
|
|
2365
2514
|
}
|
|
2366
2515
|
/** @internal */
|
|
2367
2516
|
beforeClose() {
|
|
2368
2517
|
super.beforeClose();
|
|
2518
|
+
if (this._restartDefaultTxnTimer)
|
|
2519
|
+
clearTimeout(this._restartDefaultTxnTimer);
|
|
2369
2520
|
if (this._createClassViewsOnClose) { // check for flag set during create
|
|
2370
2521
|
if (core_bentley_1.BentleyStatus.SUCCESS !== this.nativeDb.createClassViewsInDb()) {
|
|
2371
2522
|
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.SQLiteError, "Error creating class views");
|