@itwin/core-backend 5.14.0-dev.9 → 5.15.0-dev.1
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 +33 -1
- package/lib/cjs/ChangesetReader.d.ts +1 -1
- package/lib/cjs/ChangesetReader.js +2 -2
- package/lib/cjs/ChangesetReader.js.map +1 -1
- package/lib/cjs/ChangesetReaderTypes.d.ts +4 -1
- 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/ECDb.d.ts +63 -6
- package/lib/cjs/ECDb.d.ts.map +1 -1
- package/lib/cjs/ECDb.js +62 -6
- package/lib/cjs/ECDb.js.map +1 -1
- package/lib/cjs/ECSqlStatement.d.ts +2 -2
- package/lib/cjs/ECSqlStatement.js +2 -2
- package/lib/cjs/ECSqlStatement.js.map +1 -1
- package/lib/cjs/Element.d.ts.map +1 -1
- package/lib/cjs/Element.js +6 -3
- package/lib/cjs/Element.js.map +1 -1
- package/lib/cjs/IModelDb.d.ts +12 -10
- package/lib/cjs/IModelDb.d.ts.map +1 -1
- package/lib/cjs/IModelDb.js +26 -12
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/cjs/SchemaSync.d.ts +2 -2
- package/lib/cjs/SchemaSync.d.ts.map +1 -1
- package/lib/cjs/SchemaSync.js +2 -2
- 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/TxnManager.d.ts.map +1 -1
- package/lib/cjs/TxnManager.js +4 -0
- package/lib/cjs/TxnManager.js.map +1 -1
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts +90 -5
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.js +59 -1
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.js.map +1 -1
- package/lib/cjs/internal/annotations/fieldSpecs.d.ts +82 -0
- package/lib/cjs/internal/annotations/fieldSpecs.d.ts.map +1 -0
- package/lib/cjs/internal/annotations/fieldSpecs.js +187 -0
- package/lib/cjs/internal/annotations/fieldSpecs.js.map +1 -0
- package/lib/cjs/internal/annotations/fields.d.ts +39 -2
- package/lib/cjs/internal/annotations/fields.d.ts.map +1 -1
- package/lib/cjs/internal/annotations/fields.js +140 -13
- package/lib/cjs/internal/annotations/fields.js.map +1 -1
- package/lib/esm/ChangesetReader.d.ts +1 -1
- package/lib/esm/ChangesetReader.js +2 -2
- package/lib/esm/ChangesetReader.js.map +1 -1
- package/lib/esm/ChangesetReaderTypes.d.ts +4 -1
- 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/ECDb.d.ts +63 -6
- package/lib/esm/ECDb.d.ts.map +1 -1
- package/lib/esm/ECDb.js +62 -6
- package/lib/esm/ECDb.js.map +1 -1
- package/lib/esm/ECSqlStatement.d.ts +2 -2
- package/lib/esm/ECSqlStatement.js +2 -2
- package/lib/esm/ECSqlStatement.js.map +1 -1
- package/lib/esm/Element.d.ts.map +1 -1
- package/lib/esm/Element.js +6 -3
- package/lib/esm/Element.js.map +1 -1
- package/lib/esm/IModelDb.d.ts +12 -10
- package/lib/esm/IModelDb.d.ts.map +1 -1
- package/lib/esm/IModelDb.js +26 -12
- package/lib/esm/IModelDb.js.map +1 -1
- package/lib/esm/SchemaSync.d.ts +2 -2
- package/lib/esm/SchemaSync.d.ts.map +1 -1
- package/lib/esm/SchemaSync.js +2 -2
- 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/TxnManager.d.ts.map +1 -1
- package/lib/esm/TxnManager.js +4 -0
- package/lib/esm/TxnManager.js.map +1 -1
- package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts +90 -5
- package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
- package/lib/esm/annotations/ElementDrivesTextAnnotation.js +60 -2
- package/lib/esm/annotations/ElementDrivesTextAnnotation.js.map +1 -1
- package/lib/esm/internal/annotations/fieldSpecs.d.ts +82 -0
- package/lib/esm/internal/annotations/fieldSpecs.d.ts.map +1 -0
- package/lib/esm/internal/annotations/fieldSpecs.js +179 -0
- package/lib/esm/internal/annotations/fieldSpecs.js.map +1 -0
- package/lib/esm/internal/annotations/fields.d.ts +39 -2
- package/lib/esm/internal/annotations/fields.d.ts.map +1 -1
- package/lib/esm/internal/annotations/fields.js +140 -13
- package/lib/esm/internal/annotations/fields.js.map +1 -1
- package/lib/esm/test/AnnotationTestUtils.d.ts +9 -0
- package/lib/esm/test/AnnotationTestUtils.d.ts.map +1 -1
- package/lib/esm/test/AnnotationTestUtils.js +17 -0
- package/lib/esm/test/AnnotationTestUtils.js.map +1 -1
- package/lib/esm/test/annotations/FieldFormat.test.d.ts +2 -0
- package/lib/esm/test/annotations/FieldFormat.test.d.ts.map +1 -0
- package/lib/esm/test/annotations/FieldFormat.test.js +738 -0
- package/lib/esm/test/annotations/FieldFormat.test.js.map +1 -0
- package/lib/esm/test/annotations/FieldFormattingLifecycle.test.d.ts +2 -0
- package/lib/esm/test/annotations/FieldFormattingLifecycle.test.d.ts.map +1 -0
- package/lib/esm/test/annotations/FieldFormattingLifecycle.test.js +323 -0
- package/lib/esm/test/annotations/FieldFormattingLifecycle.test.js.map +1 -0
- package/lib/esm/test/annotations/FieldSpecs.test.d.ts +2 -0
- package/lib/esm/test/annotations/FieldSpecs.test.d.ts.map +1 -0
- package/lib/esm/test/annotations/FieldSpecs.test.js +54 -0
- package/lib/esm/test/annotations/FieldSpecs.test.js.map +1 -0
- package/lib/esm/test/annotations/Fields.test.js +221 -27
- package/lib/esm/test/annotations/Fields.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/ecdb/ECDb.test.js +207 -95
- package/lib/esm/test/ecdb/ECDb.test.js.map +1 -1
- package/lib/esm/test/ecdb/ECSqlAst.test.js +2 -2
- package/lib/esm/test/ecdb/ECSqlAst.test.js.map +1 -1
- package/lib/esm/test/ecdb/QueryReaders.test.js +105 -30
- package/lib/esm/test/ecdb/QueryReaders.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/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/package.json +16 -14
|
@@ -1,14 +1,51 @@
|
|
|
1
|
-
import { FieldRun,
|
|
1
|
+
import { FieldRun, RelationshipProps, TextBlock } from "@itwin/core-common";
|
|
2
2
|
import { IModelDb } from "../../IModelDb";
|
|
3
3
|
import { Id64String } from "@itwin/core-bentley";
|
|
4
|
+
import { FieldFormatting, ResolvedFieldValue } from "./fieldSpecs";
|
|
4
5
|
import type { EditTxn } from "../../EditTxn";
|
|
6
|
+
/** The per-evaluation state [[updateField]] needs: which element's fields to recompute, how to
|
|
7
|
+
* read a field's property value, and how to format quantities.
|
|
8
|
+
*/
|
|
5
9
|
export interface UpdateFieldsContext {
|
|
10
|
+
/** When set, only fields whose [FieldPropertyHost.elementId]($common) matches are recomputed —
|
|
11
|
+
* an edit to one source element leaves the annotation's other fields untouched.
|
|
12
|
+
*/
|
|
6
13
|
readonly hostElementId: Id64String | undefined;
|
|
7
|
-
|
|
14
|
+
/** Reads the value a field points at, or `undefined` if it cannot be resolved — including
|
|
15
|
+
* every field when the source element was deleted.
|
|
16
|
+
*/
|
|
17
|
+
getProperty(field: FieldRun): ResolvedFieldValue | undefined;
|
|
18
|
+
/** The formats `"quantity"` and `"coordinate"` values resolve through. [[updateField]] builds
|
|
19
|
+
* a [FormatterSpec]($core-quantity) per field from the FormatSet named by
|
|
20
|
+
* [QuantityFieldFormatOptions.formatSet]($common), falling back to the adopted FormatSet and
|
|
21
|
+
* the schemas; a value none of them can format falls back to `value.toString()`.
|
|
22
|
+
* [[createUpdateContext]] always supplies the iModel's formatting; only hand-built contexts in
|
|
23
|
+
* tests that never format a quantity leave it out.
|
|
24
|
+
*/
|
|
25
|
+
readonly formatting?: FieldFormatting;
|
|
8
26
|
}
|
|
9
27
|
export declare function createUpdateContext(hostElementId: string | undefined, iModel: IModelDb, deleted: boolean): UpdateFieldsContext;
|
|
28
|
+
/** Recomputes a single field's cached display string synchronously. Returns true iff
|
|
29
|
+
* cachedContent changed.
|
|
30
|
+
*
|
|
31
|
+
* Resolving the property value and formatting it are both fallible. A failure of either is
|
|
32
|
+
* logged and degrades *this* field to [FieldRun.invalidContentIndicator]($common); other
|
|
33
|
+
* fields in the same block are unaffected.
|
|
34
|
+
*/
|
|
10
35
|
export declare function updateField(field: FieldRun, context: UpdateFieldsContext): boolean;
|
|
36
|
+
/** Re-evaluates every [FieldRun]($common) in `textBlock` synchronously and returns the number
|
|
37
|
+
* whose cached display string changed. Fields targeting an element other than
|
|
38
|
+
* `context.hostElementId` (when set) are skipped.
|
|
39
|
+
*/
|
|
11
40
|
export declare function updateFields(textBlock: TextBlock, context: UpdateFieldsContext): number;
|
|
41
|
+
/** Re-evaluates the fields of the `props.targetId` annotation in response to a source-element
|
|
42
|
+
* change (`deleted=false`) or delete (`deleted=true`). Invoked from
|
|
43
|
+
* [[ElementDrivesTextAnnotation.onRootChangedArg]] / `onDeletedDependencyArg`.
|
|
44
|
+
*/
|
|
12
45
|
export declare function updateElementFields(props: RelationshipProps, txn: EditTxn, deleted: boolean): void;
|
|
46
|
+
/** Re-evaluates every field of the given annotation element against its current property
|
|
47
|
+
* values. Invoked from [[ElementDrivesTextAnnotation.updateFieldDependencies]] when
|
|
48
|
+
* establishing / refreshing relationships.
|
|
49
|
+
*/
|
|
13
50
|
export declare function updateAllFields(annotationElementId: Id64String, txn: EditTxn): void;
|
|
14
51
|
//# sourceMappingURL=fields.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"AAKA,OAAO,EAA0C,QAAQ,
|
|
1
|
+
{"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"AAKA,OAAO,EAA0C,QAAQ,EAA8E,iBAAiB,EAAE,SAAS,EAA8B,MAAM,oBAAoB,CAAC;AAC5N,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAyB,UAAU,EAAU,MAAM,qBAAqB,CAAC;AAKhF,OAAO,EAAE,eAAe,EAAuC,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACxG,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AA0B7C;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,QAAQ,CAAC,aAAa,EAAE,UAAU,GAAG,SAAS,CAAC;IAE/C;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,kBAAkB,GAAG,SAAS,CAAC;IAE7D;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;CACvC;AA+OD,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAM9H;AA0CD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CA0BlF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CASvF;AAyBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAElG;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,mBAAmB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAEnF"}
|
|
@@ -15,7 +15,9 @@ const BackendLoggerCategory_1 = require("../../BackendLoggerCategory");
|
|
|
15
15
|
const ElementDrivesTextAnnotation_1 = require("../../annotations/ElementDrivesTextAnnotation");
|
|
16
16
|
const ecschema_metadata_1 = require("@itwin/ecschema-metadata");
|
|
17
17
|
const ECSqlInstanceReshaper_1 = require("../ECSqlInstanceReshaper");
|
|
18
|
-
|
|
18
|
+
const fieldSpecs_1 = require("./fieldSpecs");
|
|
19
|
+
// Resolves the property a field points at into a [[ResolvedFieldValue]] — primitive value plus, for
|
|
20
|
+
// `"quantity"` / `"coordinate"` types, the property-side KoQ and persistence unit.
|
|
19
21
|
function getFieldPropertyValue(field, iModel) {
|
|
20
22
|
const host = field.propertyHost;
|
|
21
23
|
const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);
|
|
@@ -23,7 +25,7 @@ function getFieldPropertyValue(field, iModel) {
|
|
|
23
25
|
return undefined;
|
|
24
26
|
}
|
|
25
27
|
let ecClass = schemaItem;
|
|
26
|
-
const { propertyName, accessors } = field.propertyPath;
|
|
28
|
+
const { propertyName, accessors, jsonAccessors } = field.propertyPath;
|
|
27
29
|
let ecProp = ecClass.getPropertySync(propertyName);
|
|
28
30
|
if (!ecProp) {
|
|
29
31
|
return undefined;
|
|
@@ -115,15 +117,81 @@ function getFieldPropertyValue(field, iModel) {
|
|
|
115
117
|
}
|
|
116
118
|
}
|
|
117
119
|
}
|
|
120
|
+
// The ultimate result must be a primitive value.
|
|
121
|
+
if (undefined === curValue.primitive) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
// `jsonAccessors` continue the walk inside a serialized JSON string. There is no schema behind a JSON blob, so the
|
|
125
|
+
// property must declare itself as JSON via its extended type; an un-indexed JSON property resolves to the raw string.
|
|
126
|
+
if (jsonAccessors && jsonAccessors.length > 0) {
|
|
127
|
+
if (!isJsonProperty(ecProp) || typeof curValue.primitive !== "string") {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
return readJsonLeaf(curValue.primitive, jsonAccessors);
|
|
131
|
+
}
|
|
118
132
|
const propertyType = determineFieldPropertyType(ecProp);
|
|
119
133
|
if (!propertyType) {
|
|
120
134
|
return undefined;
|
|
121
135
|
}
|
|
122
|
-
//
|
|
123
|
-
|
|
136
|
+
// Property-side KoQ + persistence unit only. Overrides in `formatOptions.quantity` are
|
|
137
|
+
// merged at formatting time (see `collectFieldQuantityPairs`) so these serve as the fallback
|
|
138
|
+
// when the override doesn't resolve.
|
|
139
|
+
let kindOfQuantityFullName;
|
|
140
|
+
let persistenceUnitFullName;
|
|
141
|
+
if (propertyType === "quantity" || propertyType === "coordinate") {
|
|
142
|
+
const koq = ecProp.kindOfQuantity ? ecProp.getKindOfQuantitySync() : undefined;
|
|
143
|
+
kindOfQuantityFullName = koq?.fullName;
|
|
144
|
+
persistenceUnitFullName = koq?.persistenceUnit?.fullName;
|
|
145
|
+
}
|
|
146
|
+
return { value: curValue.primitive, type: propertyType, kindOfQuantityFullName, persistenceUnitFullName };
|
|
147
|
+
}
|
|
148
|
+
function isJsonProperty(prop) {
|
|
149
|
+
return prop.isPrimitive() && !prop.isArray() && prop.primitiveType === ecschema_metadata_1.PrimitiveType.String && prop.extendedTypeName === "Json";
|
|
150
|
+
}
|
|
151
|
+
/** Applies [FieldPropertyPath.jsonAccessors]($common) to a string property holding serialized JSON,
|
|
152
|
+
* as a walk entirely separate from the EC one: there is no schema behind a JSON blob, so no EC
|
|
153
|
+
* metadata is consulted.
|
|
154
|
+
*
|
|
155
|
+
* Returns `undefined` when `raw` is not JSON, when an accessor does not resolve, or when the path
|
|
156
|
+
* stops anywhere but a scalar — including on a JSON `null`, which is not a
|
|
157
|
+
* [FieldPrimitiveValue]($common).
|
|
158
|
+
*/
|
|
159
|
+
function readJsonLeaf(raw, accessors) {
|
|
160
|
+
let cur;
|
|
161
|
+
try {
|
|
162
|
+
cur = JSON.parse(raw);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Not valid JSON; treat as a normal string.
|
|
124
166
|
return undefined;
|
|
125
167
|
}
|
|
126
|
-
|
|
168
|
+
for (const accessor of accessors) {
|
|
169
|
+
// A number indexes an array and a string keys an object; anything else doesn't resolve.
|
|
170
|
+
const isIndex = typeof accessor === "number";
|
|
171
|
+
if (typeof cur !== "object" || null === cur || Array.isArray(cur) !== isIndex) {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
if (isIndex) {
|
|
175
|
+
cur = cur[accessor < 0 ? cur.length + accessor : accessor];
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
// Own keys only, so a key missing from the JSON can't resolve through Object.prototype.
|
|
179
|
+
cur = Object.hasOwn(cur, accessor) ? cur[accessor] : undefined;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// JSON carries no type metadata, so the leaf's JavaScript type decides the field property type.
|
|
183
|
+
// A numeric leaf is typed `"quantity"` but has no property-side KoQ, so only the field's own
|
|
184
|
+
// `kindOfQuantity` + `persistenceUnit` overrides can name a format for it.
|
|
185
|
+
switch (typeof cur) {
|
|
186
|
+
case "number":
|
|
187
|
+
return { value: cur, type: "quantity" };
|
|
188
|
+
case "boolean":
|
|
189
|
+
return { value: cur, type: "boolean" };
|
|
190
|
+
case "string":
|
|
191
|
+
return { value: cur, type: "string" };
|
|
192
|
+
default:
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
127
195
|
}
|
|
128
196
|
function determineFieldPropertyType(prop) {
|
|
129
197
|
if (prop.isEnumeration()) {
|
|
@@ -145,16 +213,18 @@ function determineFieldPropertyType(prop) {
|
|
|
145
213
|
case ecschema_metadata_1.PrimitiveType.DateTime:
|
|
146
214
|
return "datetime";
|
|
147
215
|
case ecschema_metadata_1.PrimitiveType.Double:
|
|
216
|
+
case ecschema_metadata_1.PrimitiveType.Integer:
|
|
148
217
|
case ecschema_metadata_1.PrimitiveType.Long:
|
|
218
|
+
// Any numeric property is a potential quantity. Classifying one as "quantity" is not an
|
|
219
|
+
// assertion that it *has* units -- it only decides whether the KoQ/units pipeline is
|
|
220
|
+
// consulted. A number that resolves no spec falls back to the same `toString()` the
|
|
221
|
+
// "string" formatter would have produced, so counts and identifiers still render bare.
|
|
149
222
|
return "quantity";
|
|
150
223
|
case ecschema_metadata_1.PrimitiveType.Point2d:
|
|
151
224
|
case ecschema_metadata_1.PrimitiveType.Point3d:
|
|
152
225
|
return "coordinate";
|
|
153
226
|
case ecschema_metadata_1.PrimitiveType.Binary:
|
|
154
227
|
return prop.extendedTypeName === "BeGuid" ? "string" : undefined;
|
|
155
|
-
case ecschema_metadata_1.PrimitiveType.Integer:
|
|
156
|
-
case ecschema_metadata_1.PrimitiveType.Long:
|
|
157
|
-
return "string";
|
|
158
228
|
default:
|
|
159
229
|
return undefined;
|
|
160
230
|
}
|
|
@@ -165,9 +235,53 @@ function createUpdateContext(hostElementId, iModel, deleted) {
|
|
|
165
235
|
return {
|
|
166
236
|
hostElementId,
|
|
167
237
|
getProperty: deleted ? () => undefined : (field) => getFieldPropertyValue(field, iModel),
|
|
238
|
+
formatting: (0, fieldSpecs_1.getFieldFormatting)(iModel),
|
|
168
239
|
};
|
|
169
240
|
}
|
|
170
|
-
|
|
241
|
+
/** Resolves the [FormatterSpec]($core-quantity) this field should render its magnitudes through,
|
|
242
|
+
* returning a callback bound to it, or `undefined` when not a quantity or coordinate, no formats
|
|
243
|
+
* were supplied, or no format resolves for any of the (KindOfQuantity, persistence unit) pairs the
|
|
244
|
+
* field may format through. In that last case the shortfall is logged: a persistence unit or
|
|
245
|
+
* format unit outside the bundled BIS set, a KindOfQuantity with no presentation format, or a
|
|
246
|
+
* format whose units belong to a different phenomenon than the persisted value. A
|
|
247
|
+
* `persistenceUnit` override that disagrees with the property's own unit is ignored and logged
|
|
248
|
+
* too; the property's unit is what the stored magnitude means.
|
|
249
|
+
*/
|
|
250
|
+
function resolveFormatMagnitude(value, field, context) {
|
|
251
|
+
const formatting = context.formatting;
|
|
252
|
+
if (!formatting || (value.type !== "quantity" && value.type !== "coordinate")) {
|
|
253
|
+
return undefined;
|
|
254
|
+
}
|
|
255
|
+
const quantityOptions = field.formatOptions?.quantity;
|
|
256
|
+
const { spec, candidates, ignoredPersistenceUnit } = (0, fieldSpecs_1.lookupFieldSpec)(quantityOptions, value, formatting);
|
|
257
|
+
if (ignoredPersistenceUnit) {
|
|
258
|
+
core_bentley_1.Logger.logWarning(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, "Ignoring persistenceUnit override that disagrees with the property's own persistence unit", () => ({
|
|
259
|
+
elementId: field.propertyHost.elementId,
|
|
260
|
+
propertyName: field.propertyPath.propertyName,
|
|
261
|
+
persistenceUnit: ignoredPersistenceUnit,
|
|
262
|
+
propertyPersistenceUnit: value.persistenceUnitFullName,
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
if (!spec) {
|
|
266
|
+
if (candidates.length > 0) {
|
|
267
|
+
core_bentley_1.Logger.logWarning(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, "No format resolved for text annotation field; rendering raw value", () => ({
|
|
268
|
+
elementId: field.propertyHost.elementId,
|
|
269
|
+
propertyName: field.propertyPath.propertyName,
|
|
270
|
+
formatSet: quantityOptions?.formatSet,
|
|
271
|
+
tried: candidates.map((c) => `${c.name} in ${c.persistenceUnitName}`),
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
return (magnitude) => spec.applyFormatting(magnitude);
|
|
277
|
+
}
|
|
278
|
+
/** Recomputes a single field's cached display string synchronously. Returns true iff
|
|
279
|
+
* cachedContent changed.
|
|
280
|
+
*
|
|
281
|
+
* Resolving the property value and formatting it are both fallible. A failure of either is
|
|
282
|
+
* logged and degrades *this* field to [FieldRun.invalidContentIndicator]($common); other
|
|
283
|
+
* fields in the same block are unaffected.
|
|
284
|
+
*/
|
|
171
285
|
function updateField(field, context) {
|
|
172
286
|
if (context.hostElementId && context.hostElementId !== field.propertyHost.elementId) {
|
|
173
287
|
return false;
|
|
@@ -176,7 +290,11 @@ function updateField(field, context) {
|
|
|
176
290
|
try {
|
|
177
291
|
const propValue = context.getProperty(field);
|
|
178
292
|
if (undefined !== propValue) {
|
|
179
|
-
newContent = (0, core_common_1.formatFieldValue)(
|
|
293
|
+
newContent = (0, core_common_1.formatFieldValue)({
|
|
294
|
+
value: propValue,
|
|
295
|
+
options: field.formatOptions,
|
|
296
|
+
formatMagnitude: resolveFormatMagnitude(propValue, field, context),
|
|
297
|
+
});
|
|
180
298
|
}
|
|
181
299
|
}
|
|
182
300
|
catch (err) {
|
|
@@ -189,8 +307,10 @@ function updateField(field, context) {
|
|
|
189
307
|
field.setCachedContent(newContent);
|
|
190
308
|
return true;
|
|
191
309
|
}
|
|
192
|
-
|
|
193
|
-
|
|
310
|
+
/** Re-evaluates every [FieldRun]($common) in `textBlock` synchronously and returns the number
|
|
311
|
+
* whose cached display string changed. Fields targeting an element other than
|
|
312
|
+
* `context.hostElementId` (when set) are skipped.
|
|
313
|
+
*/
|
|
194
314
|
function updateFields(textBlock, context) {
|
|
195
315
|
let numUpdated = 0;
|
|
196
316
|
for (const { child } of (0, core_common_1.traverseTextBlockComponent)(textBlock)) {
|
|
@@ -222,10 +342,17 @@ function doUpdateFields(txn, annotationId, sourceId, deleted) {
|
|
|
222
342
|
core_bentley_1.Logger.logError(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, err);
|
|
223
343
|
}
|
|
224
344
|
}
|
|
225
|
-
|
|
345
|
+
/** Re-evaluates the fields of the `props.targetId` annotation in response to a source-element
|
|
346
|
+
* change (`deleted=false`) or delete (`deleted=true`). Invoked from
|
|
347
|
+
* [[ElementDrivesTextAnnotation.onRootChangedArg]] / `onDeletedDependencyArg`.
|
|
348
|
+
*/
|
|
226
349
|
function updateElementFields(props, txn, deleted) {
|
|
227
350
|
doUpdateFields(txn, props.targetId, props.sourceId, deleted);
|
|
228
351
|
}
|
|
352
|
+
/** Re-evaluates every field of the given annotation element against its current property
|
|
353
|
+
* values. Invoked from [[ElementDrivesTextAnnotation.updateFieldDependencies]] when
|
|
354
|
+
* establishing / refreshing relationships.
|
|
355
|
+
*/
|
|
229
356
|
function updateAllFields(annotationElementId, txn) {
|
|
230
357
|
doUpdateFields(txn, annotationElementId, undefined, false);
|
|
231
358
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fields.js","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { FieldPrimitiveValue, FieldPropertyType, FieldRun, FieldValue, formatFieldValue, QueryBinder, QueryRowFormat, RelationshipProps, TextBlock, traverseTextBlockComponent } from \"@itwin/core-common\";\r\nimport { IModelDb } from \"../../IModelDb\";\r\nimport { assert, expectDefined, Id64String, Logger } from \"@itwin/core-bentley\";\r\nimport { BackendLoggerCategory } from \"../../BackendLoggerCategory\";\r\nimport { isITextAnnotation } from \"../../annotations/ElementDrivesTextAnnotation\";\r\nimport { AnyClass, EntityClass, PrimitiveType, Property, PropertyType, StructArrayProperty } from \"@itwin/ecschema-metadata\";\r\nimport { reshapePropertyValue } from \"../ECSqlInstanceReshaper\";\r\nimport type { EditTxn } from \"../../EditTxn\";\r\ninterface FieldStructValue { [key: string]: any }\r\n\r\n// An intermediate value obtained while evaluating a FieldPropertyPath.\r\ntype FieldValueType = {\r\n primitive: FieldPrimitiveValue;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct: FieldStructValue;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray: FieldPrimitiveValue[];\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray: FieldStructValue[];\r\n}\r\n\r\nexport interface UpdateFieldsContext {\r\n readonly hostElementId: Id64String | undefined;\r\n\r\n getProperty(field: FieldRun): FieldValue | undefined\r\n}\r\n\r\n// Resolve the raw primitive value of the property that a field points to.\r\nfunction getFieldPropertyValue(field: FieldRun, iModel: IModelDb): FieldValue | undefined {\r\n const host = field.propertyHost;\r\n const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);\r\n if (!EntityClass.isEntityClass(schemaItem)) {\r\n return undefined;\r\n }\r\n\r\n let ecClass: AnyClass = schemaItem;\r\n const { propertyName, accessors } = field.propertyPath;\r\n let ecProp = ecClass.getPropertySync(propertyName);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n const isAspect = ecClass.isSync(\"ElementAspect\", \"BisCore\");\r\n const where = ` WHERE ${isAspect ? \"Element.Id\" : \"ECInstanceId\"}=:elementId`;\r\n // `propertyName` may itself be a struct/array/point/navigation property, so its value can't be\r\n // decomposed into scalar sub-columns ahead of time. Query using the non-deprecated\r\n // UseECSqlPropertyNames format and reshape the value into the legacy UseJsPropertyNames shape using\r\n // ECSchema metadata (see ECSqlInstanceReshaper for why a naive, non-schema-aware rename isn't safe here).\r\n let curValue: FieldValueType | undefined = iModel.withQueryReader(`SELECT ${propertyName} FROM ${host.schemaName}.${host.className} ${where}`, (reader): FieldValueType | undefined => {\r\n if (!reader.step()) {\r\n return undefined;\r\n }\r\n\r\n const rawRootValue = reader.current[0];\r\n if (undefined === rawRootValue) {\r\n return undefined;\r\n }\r\n\r\n ecProp = expectDefined(ecProp);\r\n const rootValue = reshapePropertyValue(rawRootValue, ecProp, iModel);\r\n if (ecProp.isArray()) {\r\n return ecProp.isStruct() ? { structArray: rootValue } : { primitiveArray: rootValue };\r\n }\r\n\r\n if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n return { struct: rootValue };\r\n }\r\n\r\n if (ecProp.isPrimitive()) {\r\n return {\r\n primitive: ecProp.primitiveType === PrimitiveType.DateTime ? new Date(rootValue) : rootValue,\r\n };\r\n }\r\n\r\n return undefined;\r\n }, new QueryBinder().bindId(\"elementId\", host.elementId), { rowFormat: QueryRowFormat.UseECSqlPropertyNames });\r\n\r\n if (undefined === curValue) {\r\n return undefined;\r\n }\r\n\r\n if (accessors) {\r\n for (const accessor of accessors) {\r\n if (undefined !== curValue.primitive) {\r\n // Can't index into a primitive.\r\n return undefined;\r\n }\r\n\r\n if (typeof accessor === \"number\") {\r\n const array: FieldPrimitiveValue[] | FieldStructValue[] | undefined = curValue.primitiveArray ?? curValue.structArray;\r\n if (!array) {\r\n return undefined;\r\n }\r\n\r\n const index: number = accessor < 0 ? (array.length + accessor) : accessor;\r\n const item: FieldPrimitiveValue | FieldStructValue = array[index];\r\n if (undefined === item) {\r\n return undefined;\r\n } else if (curValue.primitiveArray) {\r\n curValue = { primitive: curValue.primitiveArray[index] };\r\n } else {\r\n assert(undefined !== curValue.structArray);\r\n assert(ecProp instanceof StructArrayProperty);\r\n\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: curValue.structArray[index] };\r\n }\r\n } else {\r\n if (undefined === curValue.struct) {\r\n return undefined;\r\n }\r\n\r\n const item: any = curValue.struct[accessor];\r\n if (undefined === item) {\r\n return undefined;\r\n }\r\n\r\n ecProp = ecClass.getPropertySync(accessor);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n if (ecProp.isArray()) {\r\n curValue = ecProp.isStruct() ? { structArray: item } : { primitiveArray: item };\r\n } else if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: item };\r\n } else if (ecProp.isPrimitive()) {\r\n curValue = { primitive: item };\r\n } else {\r\n return undefined;\r\n }\r\n }\r\n }\r\n }\r\n\r\n const propertyType = determineFieldPropertyType(ecProp);\r\n if (!propertyType) {\r\n return undefined;\r\n }\r\n\r\n // The ultimate result must be a primitive value.\r\n if (undefined === curValue.primitive) {\r\n return undefined;\r\n }\r\n\r\n return { value: curValue.primitive, type: propertyType };\r\n}\r\n\r\nfunction determineFieldPropertyType(prop: Property): FieldPropertyType | undefined {\r\n if (prop.isEnumeration()) {\r\n switch (prop.propertyType) {\r\n case PropertyType.Integer_Enumeration:\r\n return \"int-enum\";\r\n case PropertyType.String_Enumeration:\r\n return \"string-enum\";\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n if (prop.isPrimitive()) {\r\n switch (prop.primitiveType) {\r\n case PrimitiveType.Boolean:\r\n return \"boolean\";\r\n case PrimitiveType.String:\r\n return prop.extendedTypeName === \"DateTime\" ? \"datetime\" : \"string\";\r\n case PrimitiveType.DateTime:\r\n return \"datetime\";\r\n case PrimitiveType.Double:\r\n case PrimitiveType.Long:\r\n return \"quantity\";\r\n case PrimitiveType.Point2d:\r\n case PrimitiveType.Point3d:\r\n return \"coordinate\";\r\n case PrimitiveType.Binary:\r\n return prop.extendedTypeName === \"BeGuid\" ? \"string\" : undefined;\r\n case PrimitiveType.Integer:\r\n case PrimitiveType.Long:\r\n return \"string\";\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\nexport function createUpdateContext(hostElementId: string | undefined, iModel: IModelDb, deleted: boolean): UpdateFieldsContext {\r\n return {\r\n hostElementId,\r\n getProperty: deleted ? () => undefined : (field) => getFieldPropertyValue(field, iModel),\r\n };\r\n}\r\n\r\n// Recompute the display value of a single field, return false if it couldn't be evaluated.\r\nexport function updateField(field: FieldRun, context: UpdateFieldsContext): boolean {\r\n if (context.hostElementId && context.hostElementId !== field.propertyHost.elementId) {\r\n return false;\r\n }\r\n\r\n let newContent: string | undefined;\r\n try {\r\n const propValue = context.getProperty(field);\r\n if (undefined !== propValue) {\r\n newContent = formatFieldValue(propValue, field.formatOptions);\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n\r\n newContent = newContent ?? FieldRun.invalidContentIndicator;\r\n if (newContent === field.cachedContent) {\r\n return false;\r\n }\r\n\r\n field.setCachedContent(newContent);\r\n return true;\r\n}\r\n\r\n// Re-evaluates the display strings for all fields that target the element specified by `context` and returns the number\r\n// of fields whose display strings changed as a result.\r\nexport function updateFields(textBlock: TextBlock, context: UpdateFieldsContext): number {\r\n let numUpdated = 0;\r\n for (const { child } of traverseTextBlockComponent(textBlock)) {\r\n if (child.type === \"field\" && updateField(child, context)) {\r\n ++numUpdated;\r\n }\r\n }\r\n\r\n return numUpdated;\r\n}\r\n\r\nfunction doUpdateFields(txn: EditTxn, annotationId: Id64String, sourceId: Id64String | undefined, deleted: boolean): void {\r\n const iModel = txn.iModel;\r\n try {\r\n const target = iModel.elements.getElement(annotationId);\r\n if (isITextAnnotation(target)) {\r\n const context = createUpdateContext(sourceId, iModel, deleted);\r\n const updatedBlocks = [];\r\n for (const block of target.getTextBlocks()) {\r\n if (updateFields(block.textBlock, context)) {\r\n updatedBlocks.push(block);\r\n }\r\n }\r\n\r\n if (updatedBlocks.length > 0) {\r\n target.updateTextBlocks(updatedBlocks);\r\n target.update(txn);\r\n }\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n}\r\n\r\n// Invoked by ElementDrivesTextAnnotation to update fields in target element when source element changes or is deleted.\r\nexport function updateElementFields(props: RelationshipProps, txn: EditTxn, deleted: boolean): void {\r\n doUpdateFields(txn, props.targetId, props.sourceId, deleted);\r\n}\r\n\r\nexport function updateAllFields(annotationElementId: Id64String, txn: EditTxn): void {\r\n doUpdateFields(txn, annotationElementId, undefined, false);\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"fields.js","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { FieldPrimitiveValue, FieldPropertyType, FieldRun, FieldValue, formatFieldValue, FormatMagnitude, QueryBinder, QueryRowFormat, RelationshipProps, TextBlock, traverseTextBlockComponent } from \"@itwin/core-common\";\r\nimport { IModelDb } from \"../../IModelDb\";\r\nimport { assert, expectDefined, Id64String, Logger } from \"@itwin/core-bentley\";\r\nimport { BackendLoggerCategory } from \"../../BackendLoggerCategory\";\r\nimport { isITextAnnotation } from \"../../annotations/ElementDrivesTextAnnotation\";\r\nimport { AnyClass, EntityClass, PrimitiveType, Property, PropertyType, StructArrayProperty } from \"@itwin/ecschema-metadata\";\r\nimport { reshapePropertyValue } from \"../ECSqlInstanceReshaper\";\r\nimport { FieldFormatting, getFieldFormatting, lookupFieldSpec, ResolvedFieldValue } from \"./fieldSpecs\";\r\nimport type { EditTxn } from \"../../EditTxn\";\r\ninterface FieldStructValue { [key: string]: any }\r\n\r\n// An intermediate value obtained while evaluating a FieldPropertyPath.\r\ntype FieldValueType = {\r\n primitive: FieldPrimitiveValue;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct: FieldStructValue;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray: FieldPrimitiveValue[];\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray: FieldStructValue[];\r\n}\r\n\r\n/** The per-evaluation state [[updateField]] needs: which element's fields to recompute, how to\r\n * read a field's property value, and how to format quantities.\r\n */\r\nexport interface UpdateFieldsContext {\r\n /** When set, only fields whose [FieldPropertyHost.elementId]($common) matches are recomputed —\r\n * an edit to one source element leaves the annotation's other fields untouched.\r\n */\r\n readonly hostElementId: Id64String | undefined;\r\n\r\n /** Reads the value a field points at, or `undefined` if it cannot be resolved — including\r\n * every field when the source element was deleted.\r\n */\r\n getProperty(field: FieldRun): ResolvedFieldValue | undefined;\r\n\r\n /** The formats `\"quantity\"` and `\"coordinate\"` values resolve through. [[updateField]] builds\r\n * a [FormatterSpec]($core-quantity) per field from the FormatSet named by\r\n * [QuantityFieldFormatOptions.formatSet]($common), falling back to the adopted FormatSet and\r\n * the schemas; a value none of them can format falls back to `value.toString()`.\r\n * [[createUpdateContext]] always supplies the iModel's formatting; only hand-built contexts in\r\n * tests that never format a quantity leave it out.\r\n */\r\n readonly formatting?: FieldFormatting;\r\n}\r\n\r\n// Resolves the property a field points at into a [[ResolvedFieldValue]] — primitive value plus, for\r\n// `\"quantity\"` / `\"coordinate\"` types, the property-side KoQ and persistence unit.\r\nfunction getFieldPropertyValue(field: FieldRun, iModel: IModelDb): ResolvedFieldValue | undefined {\r\n const host = field.propertyHost;\r\n const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);\r\n if (!EntityClass.isEntityClass(schemaItem)) {\r\n return undefined;\r\n }\r\n\r\n let ecClass: AnyClass = schemaItem;\r\n const { propertyName, accessors, jsonAccessors } = field.propertyPath;\r\n let ecProp = ecClass.getPropertySync(propertyName);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n const isAspect = ecClass.isSync(\"ElementAspect\", \"BisCore\");\r\n const where = ` WHERE ${isAspect ? \"Element.Id\" : \"ECInstanceId\"}=:elementId`;\r\n // `propertyName` may itself be a struct/array/point/navigation property, so its value can't be\r\n // decomposed into scalar sub-columns ahead of time. Query using the non-deprecated\r\n // UseECSqlPropertyNames format and reshape the value into the legacy UseJsPropertyNames shape using\r\n // ECSchema metadata (see ECSqlInstanceReshaper for why a naive, non-schema-aware rename isn't safe here).\r\n let curValue: FieldValueType | undefined = iModel.withQueryReader(`SELECT ${propertyName} FROM ${host.schemaName}.${host.className} ${where}`, (reader): FieldValueType | undefined => {\r\n if (!reader.step()) {\r\n return undefined;\r\n }\r\n\r\n const rawRootValue = reader.current[0];\r\n if (undefined === rawRootValue) {\r\n return undefined;\r\n }\r\n\r\n ecProp = expectDefined(ecProp);\r\n const rootValue = reshapePropertyValue(rawRootValue, ecProp, iModel);\r\n if (ecProp.isArray()) {\r\n return ecProp.isStruct() ? { structArray: rootValue } : { primitiveArray: rootValue };\r\n }\r\n\r\n if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n return { struct: rootValue };\r\n }\r\n\r\n if (ecProp.isPrimitive()) {\r\n return {\r\n primitive: ecProp.primitiveType === PrimitiveType.DateTime ? new Date(rootValue) : rootValue,\r\n };\r\n }\r\n\r\n return undefined;\r\n }, new QueryBinder().bindId(\"elementId\", host.elementId), { rowFormat: QueryRowFormat.UseECSqlPropertyNames });\r\n\r\n if (undefined === curValue) {\r\n return undefined;\r\n }\r\n\r\n if (accessors) {\r\n for (const accessor of accessors) {\r\n if (undefined !== curValue.primitive) {\r\n // Can't index into a primitive.\r\n return undefined;\r\n }\r\n\r\n if (typeof accessor === \"number\") {\r\n const array: FieldPrimitiveValue[] | FieldStructValue[] | undefined = curValue.primitiveArray ?? curValue.structArray;\r\n if (!array) {\r\n return undefined;\r\n }\r\n\r\n const index: number = accessor < 0 ? (array.length + accessor) : accessor;\r\n const item: FieldPrimitiveValue | FieldStructValue = array[index];\r\n if (undefined === item) {\r\n return undefined;\r\n } else if (curValue.primitiveArray) {\r\n curValue = { primitive: curValue.primitiveArray[index] };\r\n } else {\r\n assert(undefined !== curValue.structArray);\r\n assert(ecProp instanceof StructArrayProperty);\r\n\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: curValue.structArray[index] };\r\n }\r\n } else {\r\n if (undefined === curValue.struct) {\r\n return undefined;\r\n }\r\n\r\n const item: any = curValue.struct[accessor];\r\n if (undefined === item) {\r\n return undefined;\r\n }\r\n\r\n ecProp = ecClass.getPropertySync(accessor);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n if (ecProp.isArray()) {\r\n curValue = ecProp.isStruct() ? { structArray: item } : { primitiveArray: item };\r\n } else if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: item };\r\n } else if (ecProp.isPrimitive()) {\r\n curValue = { primitive: item };\r\n } else {\r\n return undefined;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The ultimate result must be a primitive value.\r\n if (undefined === curValue.primitive) {\r\n return undefined;\r\n }\r\n\r\n // `jsonAccessors` continue the walk inside a serialized JSON string. There is no schema behind a JSON blob, so the\r\n // property must declare itself as JSON via its extended type; an un-indexed JSON property resolves to the raw string.\r\n if (jsonAccessors && jsonAccessors.length > 0) {\r\n if (!isJsonProperty(ecProp) || typeof curValue.primitive !== \"string\") {\r\n return undefined;\r\n }\r\n\r\n return readJsonLeaf(curValue.primitive, jsonAccessors);\r\n }\r\n\r\n const propertyType = determineFieldPropertyType(ecProp);\r\n if (!propertyType) {\r\n return undefined;\r\n }\r\n\r\n // Property-side KoQ + persistence unit only. Overrides in `formatOptions.quantity` are\r\n // merged at formatting time (see `collectFieldQuantityPairs`) so these serve as the fallback\r\n // when the override doesn't resolve.\r\n let kindOfQuantityFullName: string | undefined;\r\n let persistenceUnitFullName: string | undefined;\r\n if (propertyType === \"quantity\" || propertyType === \"coordinate\") {\r\n const koq = ecProp.kindOfQuantity ? ecProp.getKindOfQuantitySync() : undefined;\r\n kindOfQuantityFullName = koq?.fullName;\r\n persistenceUnitFullName = koq?.persistenceUnit?.fullName;\r\n }\r\n\r\n return { value: curValue.primitive, type: propertyType, kindOfQuantityFullName, persistenceUnitFullName };\r\n}\r\n\r\nfunction isJsonProperty(prop: Property): boolean {\r\n return prop.isPrimitive() && !prop.isArray() && prop.primitiveType === PrimitiveType.String && prop.extendedTypeName === \"Json\";\r\n}\r\n\r\n/** Applies [FieldPropertyPath.jsonAccessors]($common) to a string property holding serialized JSON,\r\n * as a walk entirely separate from the EC one: there is no schema behind a JSON blob, so no EC\r\n * metadata is consulted.\r\n *\r\n * Returns `undefined` when `raw` is not JSON, when an accessor does not resolve, or when the path\r\n * stops anywhere but a scalar — including on a JSON `null`, which is not a\r\n * [FieldPrimitiveValue]($common).\r\n */\r\nfunction readJsonLeaf(raw: string, accessors: ReadonlyArray<string | number>): FieldValue | undefined {\r\n let cur: any;\r\n try {\r\n cur = JSON.parse(raw);\r\n } catch {\r\n // Not valid JSON; treat as a normal string.\r\n return undefined;\r\n }\r\n\r\n for (const accessor of accessors) {\r\n // A number indexes an array and a string keys an object; anything else doesn't resolve.\r\n const isIndex = typeof accessor === \"number\";\r\n if (typeof cur !== \"object\" || null === cur || Array.isArray(cur) !== isIndex) {\r\n return undefined;\r\n }\r\n\r\n if (isIndex) {\r\n cur = cur[accessor < 0 ? cur.length + accessor : accessor];\r\n } else {\r\n // Own keys only, so a key missing from the JSON can't resolve through Object.prototype.\r\n cur = Object.hasOwn(cur, accessor) ? cur[accessor] : undefined;\r\n }\r\n }\r\n\r\n // JSON carries no type metadata, so the leaf's JavaScript type decides the field property type.\r\n // A numeric leaf is typed `\"quantity\"` but has no property-side KoQ, so only the field's own\r\n // `kindOfQuantity` + `persistenceUnit` overrides can name a format for it.\r\n switch (typeof cur) {\r\n case \"number\":\r\n return { value: cur, type: \"quantity\" };\r\n case \"boolean\":\r\n return { value: cur, type: \"boolean\" };\r\n case \"string\":\r\n return { value: cur, type: \"string\" };\r\n default:\r\n return undefined;\r\n }\r\n}\r\n\r\nfunction determineFieldPropertyType(prop: Property): FieldPropertyType | undefined {\r\n if (prop.isEnumeration()) {\r\n switch (prop.propertyType) {\r\n case PropertyType.Integer_Enumeration:\r\n return \"int-enum\";\r\n case PropertyType.String_Enumeration:\r\n return \"string-enum\";\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n if (prop.isPrimitive()) {\r\n switch (prop.primitiveType) {\r\n case PrimitiveType.Boolean:\r\n return \"boolean\";\r\n case PrimitiveType.String:\r\n return prop.extendedTypeName === \"DateTime\" ? \"datetime\" : \"string\";\r\n case PrimitiveType.DateTime:\r\n return \"datetime\";\r\n case PrimitiveType.Double:\r\n case PrimitiveType.Integer:\r\n case PrimitiveType.Long:\r\n // Any numeric property is a potential quantity. Classifying one as \"quantity\" is not an\r\n // assertion that it *has* units -- it only decides whether the KoQ/units pipeline is\r\n // consulted. A number that resolves no spec falls back to the same `toString()` the\r\n // \"string\" formatter would have produced, so counts and identifiers still render bare.\r\n return \"quantity\";\r\n case PrimitiveType.Point2d:\r\n case PrimitiveType.Point3d:\r\n return \"coordinate\";\r\n case PrimitiveType.Binary:\r\n return prop.extendedTypeName === \"BeGuid\" ? \"string\" : undefined;\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\nexport function createUpdateContext(hostElementId: string | undefined, iModel: IModelDb, deleted: boolean): UpdateFieldsContext {\r\n return {\r\n hostElementId,\r\n getProperty: deleted ? () => undefined : (field) => getFieldPropertyValue(field, iModel),\r\n formatting: getFieldFormatting(iModel),\r\n };\r\n}\r\n\r\n/** Resolves the [FormatterSpec]($core-quantity) this field should render its magnitudes through,\r\n * returning a callback bound to it, or `undefined` when not a quantity or coordinate, no formats\r\n * were supplied, or no format resolves for any of the (KindOfQuantity, persistence unit) pairs the\r\n * field may format through. In that last case the shortfall is logged: a persistence unit or\r\n * format unit outside the bundled BIS set, a KindOfQuantity with no presentation format, or a\r\n * format whose units belong to a different phenomenon than the persisted value. A\r\n * `persistenceUnit` override that disagrees with the property's own unit is ignored and logged\r\n * too; the property's unit is what the stored magnitude means.\r\n */\r\nfunction resolveFormatMagnitude(value: ResolvedFieldValue, field: FieldRun, context: UpdateFieldsContext): FormatMagnitude | undefined {\r\n const formatting = context.formatting;\r\n if (!formatting || (value.type !== \"quantity\" && value.type !== \"coordinate\")) {\r\n return undefined;\r\n }\r\n\r\n const quantityOptions = field.formatOptions?.quantity;\r\n const { spec, candidates, ignoredPersistenceUnit } = lookupFieldSpec(quantityOptions, value, formatting);\r\n if (ignoredPersistenceUnit) {\r\n Logger.logWarning(BackendLoggerCategory.IModelDb, \"Ignoring persistenceUnit override that disagrees with the property's own persistence unit\", () => ({\r\n elementId: field.propertyHost.elementId,\r\n propertyName: field.propertyPath.propertyName,\r\n persistenceUnit: ignoredPersistenceUnit,\r\n propertyPersistenceUnit: value.persistenceUnitFullName,\r\n }));\r\n }\r\n if (!spec) {\r\n if (candidates.length > 0) {\r\n Logger.logWarning(BackendLoggerCategory.IModelDb, \"No format resolved for text annotation field; rendering raw value\", () => ({\r\n elementId: field.propertyHost.elementId,\r\n propertyName: field.propertyPath.propertyName,\r\n formatSet: quantityOptions?.formatSet,\r\n tried: candidates.map((c) => `${c.name} in ${c.persistenceUnitName}`),\r\n }));\r\n }\r\n return undefined;\r\n }\r\n\r\n return (magnitude) => spec.applyFormatting(magnitude);\r\n}\r\n\r\n/** Recomputes a single field's cached display string synchronously. Returns true iff\r\n * cachedContent changed.\r\n *\r\n * Resolving the property value and formatting it are both fallible. A failure of either is\r\n * logged and degrades *this* field to [FieldRun.invalidContentIndicator]($common); other\r\n * fields in the same block are unaffected.\r\n */\r\nexport function updateField(field: FieldRun, context: UpdateFieldsContext): boolean {\r\n if (context.hostElementId && context.hostElementId !== field.propertyHost.elementId) {\r\n return false;\r\n }\r\n\r\n let newContent: string | undefined;\r\n try {\r\n const propValue = context.getProperty(field);\r\n if (undefined !== propValue) {\r\n newContent = formatFieldValue({\r\n value: propValue,\r\n options: field.formatOptions,\r\n formatMagnitude: resolveFormatMagnitude(propValue, field, context),\r\n });\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n\r\n newContent = newContent ?? FieldRun.invalidContentIndicator;\r\n if (newContent === field.cachedContent) {\r\n return false;\r\n }\r\n\r\n field.setCachedContent(newContent);\r\n return true;\r\n}\r\n\r\n/** Re-evaluates every [FieldRun]($common) in `textBlock` synchronously and returns the number\r\n * whose cached display string changed. Fields targeting an element other than\r\n * `context.hostElementId` (when set) are skipped.\r\n */\r\nexport function updateFields(textBlock: TextBlock, context: UpdateFieldsContext): number {\r\n let numUpdated = 0;\r\n for (const { child } of traverseTextBlockComponent(textBlock)) {\r\n if (child.type === \"field\" && updateField(child, context)) {\r\n ++numUpdated;\r\n }\r\n }\r\n\r\n return numUpdated;\r\n}\r\n\r\nfunction doUpdateFields(txn: EditTxn, annotationId: Id64String, sourceId: Id64String | undefined, deleted: boolean): void {\r\n const iModel = txn.iModel;\r\n try {\r\n const target = iModel.elements.getElement(annotationId);\r\n if (isITextAnnotation(target)) {\r\n const context = createUpdateContext(sourceId, iModel, deleted);\r\n const updatedBlocks = [];\r\n for (const block of target.getTextBlocks()) {\r\n if (updateFields(block.textBlock, context)) {\r\n updatedBlocks.push(block);\r\n }\r\n }\r\n\r\n if (updatedBlocks.length > 0) {\r\n target.updateTextBlocks(updatedBlocks);\r\n target.update(txn);\r\n }\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n}\r\n\r\n/** Re-evaluates the fields of the `props.targetId` annotation in response to a source-element\r\n * change (`deleted=false`) or delete (`deleted=true`). Invoked from\r\n * [[ElementDrivesTextAnnotation.onRootChangedArg]] / `onDeletedDependencyArg`.\r\n */\r\nexport function updateElementFields(props: RelationshipProps, txn: EditTxn, deleted: boolean): void {\r\n doUpdateFields(txn, props.targetId, props.sourceId, deleted);\r\n}\r\n\r\n/** Re-evaluates every field of the given annotation element against its current property\r\n * values. Invoked from [[ElementDrivesTextAnnotation.updateFieldDependencies]] when\r\n * establishing / refreshing relationships.\r\n */\r\nexport function updateAllFields(annotationElementId: Id64String, txn: EditTxn): void {\r\n doUpdateFields(txn, annotationElementId, undefined, false);\r\n}\r\n"]}
|
|
@@ -188,7 +188,7 @@ export declare class ChangesetReader implements Disposable, ChangeSource {
|
|
|
188
188
|
* Increasing the batch size improves throughput at the cost of higher peak memory; decreasing it keeps memory consumption lower.
|
|
189
189
|
*
|
|
190
190
|
* Default batch sizes when `setBatchSize` is not called:
|
|
191
|
-
* - `InstanceKey` filter: **100**.
|
|
191
|
+
* - `InstanceKey` or `InstanceKeyAndIdentifiers` filter: **100**.
|
|
192
192
|
* - `BisCoreElement` filter (any `abbreviateBlobs` setting): **20**.
|
|
193
193
|
* - `All` filter, `abbreviateBlobs: false`: **5**.
|
|
194
194
|
* - `All` filter (blobs abbreviated or unset): **10**.
|
|
@@ -62,7 +62,7 @@ export class ChangesetReader {
|
|
|
62
62
|
get _batchSize() {
|
|
63
63
|
if (this._batchSizeOverride !== undefined)
|
|
64
64
|
return this._batchSizeOverride;
|
|
65
|
-
if (this._propFilter === PropertyFilter.InstanceKey)
|
|
65
|
+
if (this._propFilter === PropertyFilter.InstanceKey || this._propFilter === PropertyFilter.InstanceKeyAndIdentifiers)
|
|
66
66
|
return 100;
|
|
67
67
|
if (this._propFilter === PropertyFilter.BisCoreElement)
|
|
68
68
|
return 20; // because BisCore Element class do not contain any GeomStream property so abbreviateBlobs is not relevant here
|
|
@@ -330,7 +330,7 @@ export class ChangesetReader {
|
|
|
330
330
|
* Increasing the batch size improves throughput at the cost of higher peak memory; decreasing it keeps memory consumption lower.
|
|
331
331
|
*
|
|
332
332
|
* Default batch sizes when `setBatchSize` is not called:
|
|
333
|
-
* - `InstanceKey` filter: **100**.
|
|
333
|
+
* - `InstanceKey` or `InstanceKeyAndIdentifiers` filter: **100**.
|
|
334
334
|
* - `BisCoreElement` filter (any `abbreviateBlobs` setting): **20**.
|
|
335
335
|
* - `All` filter, `abbreviateBlobs: false`: **5**.
|
|
336
336
|
* - `All` filter (blobs abbreviated or unset): **10**.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChangesetReader.js","sourceRoot":"","sources":["../../src/ChangesetReader.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module ECDb\r\n */\r\nimport { DbOpcode, Id64String, IModelStatus } from \"@itwin/core-bentley\";\r\nimport { IModelError } from \"@itwin/core-common\";\r\nimport { IModelDb } from \"./IModelDb\";\r\nimport { IModelNative } from \"./internal/NativePlatform\";\r\nimport { _nativeDb } from \"./internal/Symbols\";\r\nimport { IModelJsNative } from \"@bentley/imodeljs-native\";\r\nimport { ChangeInstance, ChangesetReaderArgs, ChangeSource, PropertyFilter, RowFormatOptions } from \"./ChangesetReaderTypes\";\r\nimport { AnyDb, SqliteChangeOp } from \"./SqliteChangesetReader\";\r\n\r\n\r\n// ---------------------------------------------------------------------------\r\n// ChangesetReader\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Reads EC-typed changeset data natively from a changeset file, changeset group,\r\n * in-memory transaction, or local un-pushed changes.\r\n *\r\n * Implements [ChangeSource]($backend) so rows can be fed directly into\r\n * [PartialChangeUnifier]($backend) to merge partial (per-table) instances into\r\n * complete EC instances.\r\n *\r\n * When the current row is a non-EC internal SQLite table, [[isECTable]] is `false`\r\n * and both [[inserted]] and [[deleted]] remain `undefined`.\r\n *\r\n * @note The native reader operates one SQLite table-row at a time. Multi-table EC\r\n * instances must be merged using [PartialChangeUnifier]($backend).\r\n * @beta\r\n */\r\nexport class ChangesetReader implements Disposable, ChangeSource {\r\n private static readonly defaultSpillThresholdInBytes = 50 * 1024 * 1024; // 50 MiB\r\n private readonly _nativeReader: IModelJsNative.ChangesetReader = new IModelNative.platform.ChangesetReader();\r\n // Internal options — keep ECClassId as raw Id so the unifier can use it as-is.\r\n private _rowOptions?: RowFormatOptions;\r\n private _batchSizeOverride?: number;\r\n private _propFilter: PropertyFilter = PropertyFilter.All;\r\n private _changeIndex = 0;\r\n /** Rows fetched in the most recent native batch call. */\r\n private _cache: IModelJsNative.ChangesetRowData[] = [];\r\n /**\r\n * Index of the current row in `_cache`.\r\n * Equals `_cache.length` (i.e. out-of-bounds) when no row is active:\r\n * initial state, after exhaustion, or after close().\r\n */\r\n private _cacheIndex = 0;\r\n /** Cached result of the `inserted` getter for the current row. `undefined` when not yet computed or not applicable. */\r\n private _cachedInserted: ChangeInstance | undefined = undefined;\r\n /** Cached result of the `deleted` getter for the current row. `undefined` when not yet computed or not applicable. */\r\n private _cachedDeleted: ChangeInstance | undefined = undefined;\r\n\r\n /** The db used for EC schema resolution. */\r\n public readonly db: AnyDb;\r\n\r\n /** Returns the active cached row, throwing if no row is current.\r\n * @internal */\r\n private get _currentRow(): IModelJsNative.ChangesetRowData {\r\n if (this._cacheIndex >= this._cache.length)\r\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader: no current row — call step() first.\");\r\n return this._cache[this._cacheIndex];\r\n }\r\n\r\n /** Returns the batch size to use for native step() calls based on the active property filter.\r\n * @internal */\r\n private get _batchSize(): number {\r\n if (this._batchSizeOverride !== undefined) return this._batchSizeOverride;\r\n if (this._propFilter === PropertyFilter.InstanceKey) return 100;\r\n if (this._propFilter === PropertyFilter.BisCoreElement) return 20; // because BisCore Element class do not contain any GeomStream property so abbreviateBlobs is not relevant here\r\n if (this._rowOptions?.abbreviateBlobs === false) return 5;\r\n return 10; // PropertyFilter.All\r\n }\r\n\r\n /**\r\n * `true` when the current row belongs to an EC-mapped table.\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get isECTable(): boolean { return this._currentRow.metadata.isECTable; }\r\n\r\n /**\r\n * Name of the SQLite table for the current change row.\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get tableName(): string { return this._currentRow.metadata.tableName; }\r\n\r\n /**\r\n * `true` when the current change was applied indirectly\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get isIndirectChange(): boolean { return this._currentRow.metadata.isIndirectChange; }\r\n\r\n /**\r\n * Post-change (inserted or updated-new) EC instance, computed lazily after each [[step]] call.\r\n * `undefined` when the current row is a Delete or a non-EC table row or [[step]] returned false.\r\n * For UPDATE,inserted instances indicate the new state of the instance after the change has been applied and\r\n * deleted instances indicate the old state of the instance before the change has been applied.\r\n * For INSERT, inserted instances indicate the new state of the instance after the change has been applied and deleted instances are undefined.\r\n * For DELETE, deleted instances indicate the old state of the instance before the change has been applied and inserted instances are undefined.\r\n * @beta\r\n */\r\n public get inserted(): ChangeInstance | undefined {\r\n if (this._cachedInserted !== undefined) return this._cachedInserted;\r\n const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;\r\n if (!row || !row.newValues)\r\n return undefined;\r\n const op = this.op;\r\n return (this._cachedInserted = {\r\n ...row.newValues.data,\r\n $meta: {\r\n op,\r\n tables: [row.metadata.tableName],\r\n changeIndexes: [this._changeIndex],\r\n stage: \"New\",\r\n instanceKey: row.newValues.key,\r\n propFilter: this._propFilter,\r\n changeFetchedPropNames: row.newValues.changeFetchedPropNames,\r\n rowOptions: this._rowOptions,\r\n isIndirectChange: row.metadata.isIndirectChange,\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Pre-change (deleted or updated-old) EC instance, computed lazily after each [[step]] call.\r\n * `undefined` when the current row is an Insert or a non-EC table row or [[step]] returned false.\r\n * @beta\r\n */\r\n public get deleted(): ChangeInstance | undefined {\r\n if (this._cachedDeleted !== undefined) return this._cachedDeleted;\r\n const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;\r\n if (!row || !row.oldValues)\r\n return undefined;\r\n const op = this.op;\r\n return (this._cachedDeleted = {\r\n ...row.oldValues.data,\r\n $meta: {\r\n op,\r\n tables: [row.metadata.tableName],\r\n changeIndexes: [this._changeIndex],\r\n stage: \"Old\",\r\n instanceKey: row.oldValues.key,\r\n propFilter: this._propFilter,\r\n changeFetchedPropNames: row.oldValues.changeFetchedPropNames,\r\n rowOptions: this._rowOptions,\r\n isIndirectChange: row.metadata.isIndirectChange,\r\n },\r\n });\r\n }\r\n\r\n // Private — callers use static factory methods.\r\n private constructor(db: AnyDb) {\r\n this.db = db;\r\n }\r\n\r\n /** Map public RowFormatOptions to the native adaptor options.\r\n * @internal */\r\n private toNativeRowOptions(opts: RowFormatOptions): IModelJsNative.ECSqlRowAdaptorOptions {\r\n return {\r\n abbreviateBlobs: opts.abbreviateBlobs,\r\n classIdsToClassNames: opts.classIdsToClassNames,\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n useJsName: opts.useJsName,\r\n };\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Static factory methods\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Open a changeset file from disk.\r\n * @param args.fileName Absolute path to the changeset file.\r\n * @param args.db Database at or after the changeset's ending state, used for schema resolution.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @throws if the native layer fails to open the file.\r\n * @beta\r\n */\r\n public static openFile(args: { readonly fileName: string } & ChangesetReaderArgs): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openFile(args.db[_nativeDb], args.fileName, args.invert ?? false, reader._propFilter);\r\n }\r\n catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Concatenate multiple changeset files and read them as a single logical stream.\r\n * @param args.changesetFiles Ordered list of changeset file paths.\r\n * @param args.db Database with schema at or ahead of the last changeset.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of the changeset data in the change group exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for processing large changeset groups under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if `changesetFiles` is empty, or if the native layer fails to open\r\n * the group.\r\n * @beta\r\n */\r\n public static openGroup(args: { readonly changesetFiles: string[], spillThresholdInBytes?: number } & ChangesetReaderArgs): ChangesetReader {\r\n if (args.changesetFiles.length === 0)\r\n throw new IModelError(IModelStatus.BadArg, \"changesetFiles must contain at least one file.\");\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openGroup(args.db[_nativeDb], args.changesetFiles, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n }\r\n catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Read pending (not yet pushed) local changes from an open IModelDb.\r\n * @param args.db Must be an [IModelDb]($backend) (not [ECDb]($backend)).\r\n * @param args.includeInMemoryChanges Also include in-memory (not yet saved to disk) changes.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of all local un-pushed saved changes exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for iModels with large local change backlogs under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if the native layer\r\n * fails to open the local changes.\r\n * @beta\r\n */\r\n public static openLocalChanges(\r\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb; includeInMemoryChanges?: boolean, spillThresholdInBytes?: number },\r\n ): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openLocalChanges(args.db[_nativeDb], args.includeInMemoryChanges ?? false, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n } catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Read the in-memory (not yet saved to disk) changes of an open IModelDb.\r\n * @param args.db Must be an [IModelDb]($backend).\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of the in-memory (unsaved) change data exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for large in-memory transactions under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if the native layer encounters an error while opening the in-memory changes.\r\n * @beta\r\n */\r\n public static openInMemoryChanges(\r\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb, spillThresholdInBytes?: number },\r\n ): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openInMemoryChanges(args.db[_nativeDb], args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n } catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Read a single saved transaction by its id.\r\n * @param args.db Must be an [IModelDb]($backend) ([ECDb]($backend) does not support transactions).\r\n * @param args.txnId The id of the saved transaction to read.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of the transaction's change data exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for large transactions under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if `txnId` is not found, or\r\n * the native layer fails to open the transaction data.\r\n * @beta\r\n */\r\n public static openTxn(\r\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb; txnId: Id64String, spillThresholdInBytes?: number },\r\n ): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openTxn(args.db[_nativeDb], args.txnId, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n } catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /** Throws if [[step]] has already been called, preventing filter/mode changes mid-iteration.\r\n * @internal */\r\n private throwIfAlreadyStepped(): void {\r\n if (this._changeIndex > 0)\r\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader: filters and strict mode and batch size must be configured before the first call to step().\");\r\n }\r\n\r\n /** Handle errors that occur while auto closing the reader if there is also an error while opening the reader */\r\n private handleCloseErrorWhileOpening(e: unknown): void {\r\n try {\r\n this.close();\r\n } catch (closeError) {\r\n throw new IModelError(IModelStatus.BadArg, `Failed to open ChangesetReader with error ${e instanceof Error ? e.message : String(e)}.\r\n Additionally, that triggered an automatic closure of the reader\r\n releasing native resources which also failed with failure ${closeError instanceof Error ? closeError.message : String(closeError)}.\r\n Check native error logs for more details.`);\r\n }\r\n }\r\n\r\n /**\r\n * Set the number of rows to fetch and cache while stepping.\r\n * This is an advanced option that can be used to tune performance for large changesets.\r\n * Increasing the batch size improves throughput at the cost of higher peak memory; decreasing it keeps memory consumption lower.\r\n *\r\n * Default batch sizes when `setBatchSize` is not called:\r\n * - `InstanceKey` filter: **100**.\r\n * - `BisCoreElement` filter (any `abbreviateBlobs` setting): **20**.\r\n * - `All` filter, `abbreviateBlobs: false`: **5**.\r\n * - `All` filter (blobs abbreviated or unset): **10**.\r\n *\r\n * @param batchSize Number of rows to fetch and cache while stepping. Must be a positive integer.\r\n * @throws [[IModelError]] if [[step]] has already been called successfully, or if `batchSize` is not a positive integer.\r\n * @beta\r\n */\r\n public setBatchSize(batchSize: number): void {\r\n this.throwIfAlreadyStepped();\r\n if (!Number.isInteger(batchSize) || batchSize <= 0)\r\n throw new IModelError(IModelStatus.BadArg, \"ChangesetReader: batchSize must be a positive integer.\");\r\n this._batchSizeOverride = batchSize;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Filtering\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Restrict iteration to changes from the named SQLite tables.\r\n * That means the rows for changes from other tables will be skipped entirely and won't be visible through the reader.\r\n * @param tableNames SQLite table names to include.\r\n * Note: Table names must be provided in the correct case for proper filtering.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error while setting the filter.\r\n * @beta\r\n */\r\n public setTableNameFilters(tableNames: Set<string>): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.setTableNameFilters(Array.from(tableNames));\r\n }\r\n\r\n /**\r\n * Restrict iteration to changes with the given operation types.\r\n * That means the rows for changes with other operation types will be skipped entirely and won't be visible through the reader.\r\n * @param ops Operations to include.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error while setting the filter.\r\n * @beta\r\n */\r\n public setOpCodeFilters(ops: Set<SqliteChangeOp>): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.setOpCodeFilters(Array.from(ops));\r\n }\r\n\r\n /**\r\n * Restrict iteration to changes for the given EC class names.\r\n * That means the rows for changes from other EC classes will be skipped entirely and won't be visible through the reader.\r\n * @param classNames EC class names to include. The classNames should be in the full name format(i.e. \"SchemaName:ClassName\").\r\n * Note: Schema names and class names must be provided in the correct case for proper filtering. Derived classes are not automatically included, so they must be specified explicitly if needed.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error while setting the filter.\r\n * @beta\r\n */\r\n public setClassNameFilters(classNames: Set<string>): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.setClassNameFilters(Array.from(classNames));\r\n }\r\n\r\n /**\r\n * Remove the table-name filters\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public clearTableNameFilters(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.clearTableNameFilters();\r\n }\r\n\r\n /**\r\n * Remove the op-code filters\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public clearOpCodeFilters(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.clearOpCodeFilters();\r\n }\r\n\r\n /**\r\n * Remove the class-name filters\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public clearClassNameFilters(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.clearClassNameFilters();\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Strict mode\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Enable strict mode on the reader.\r\n *\r\n * Strict mode affects how the reader handles a **column-count mismatch** between a change\r\n * record and the corresponding live database table. Such a mismatch can occur when columns\r\n * have been added to a table after the changeset was created.\r\n *\r\n * When strict mode is **enabled**: if the number of columns recorded in a change row differs\r\n * from the number of columns currently present in the live table, the reader throws an error\r\n * instead of processing that row.\r\n *\r\n * Use strict mode when you need to be certain that every change row is interpreted against\r\n * exactly the schema that was in effect when the changeset was written.\r\n *\r\n * @see [[disableStrictMode]] — the default (lenient) behaviour.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public enableStrictMode(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.enableStrictMode();\r\n }\r\n\r\n /**\r\n * Disable strict mode on the reader (this is the default).\r\n *\r\n * When strict mode is **disabled**: if the number of columns recorded in a change row differs\r\n * from the number of columns currently present in the live table, the reader takes the\r\n * **minimum** of the two column counts and proceeds normally with that subset. This is safe\r\n * because SQLite only ever appends new columns at the end of a table and never removes them —\r\n * so older change records simply lack the trailing columns that were added later, and those\r\n * missing columns are silently ignored.\r\n *\r\n * @see [[enableStrictMode]] — throw on column-count mismatches instead.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public disableStrictMode(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.disableStrictMode();\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Iteration\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Advance to the next change.\r\n * @returns `true` while positioned on a valid change; `false` when the stream is exhausted.\r\n * @throws if the native layer encounters an error while reading or decoding\r\n * the next change.\r\n * @beta\r\n */\r\n public step(): boolean {\r\n this._cachedInserted = undefined;\r\n this._cachedDeleted = undefined;\r\n if (this._cacheIndex + 1 < this._cache.length) {\r\n // Still have rows in cache — advance the pointer\r\n this._cacheIndex++;\r\n } else {\r\n // Cache empty or fully consumed — fetch next batch from native\r\n const nativeRowOpts = this._rowOptions ? this.toNativeRowOptions(this._rowOptions) : {};\r\n this._cache = this._nativeReader.step(this._batchSize, nativeRowOpts);\r\n this._cacheIndex = 0;\r\n if (this._cache.length === 0) return false;\r\n }\r\n this._changeIndex++;\r\n return true;\r\n }\r\n\r\n /**\r\n * SQLite opcode of the current change.\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get op(): SqliteChangeOp {\r\n const opCode = this._currentRow.metadata.opCode;\r\n return opCode === DbOpcode.Insert ? \"Inserted\"\r\n : opCode === DbOpcode.Update ? \"Updated\"\r\n : \"Deleted\";\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Lifecycle\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Close the reader and release all native resources.\r\n *\r\n * @throws if the native layer encounters an error during cleanup. Native resources\r\n * are not fully released when this throws — check the native error\r\n * logs for details.\r\n * @beta\r\n */\r\n public close(): void {\r\n this._changeIndex = 0;\r\n this._cache = [];\r\n this._cacheIndex = 0;\r\n this._cachedInserted = undefined;\r\n this._cachedDeleted = undefined;\r\n this._nativeReader.close();\r\n }\r\n\r\n /**\r\n * Implements the `Disposable` contract — delegates to [[close]].\r\n *\r\n * @throws if the native layer fails to release its resources (re-thrown from [[close]]).\r\n * @beta\r\n */\r\n public [Symbol.dispose](): void {\r\n this.close();\r\n }\r\n}\r\n\r\n"]}
|
|
1
|
+
{"version":3,"file":"ChangesetReader.js","sourceRoot":"","sources":["../../src/ChangesetReader.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module ECDb\r\n */\r\nimport { DbOpcode, Id64String, IModelStatus } from \"@itwin/core-bentley\";\r\nimport { IModelError } from \"@itwin/core-common\";\r\nimport { IModelDb } from \"./IModelDb\";\r\nimport { IModelNative } from \"./internal/NativePlatform\";\r\nimport { _nativeDb } from \"./internal/Symbols\";\r\nimport { IModelJsNative } from \"@bentley/imodeljs-native\";\r\nimport { ChangeInstance, ChangesetReaderArgs, ChangeSource, PropertyFilter, RowFormatOptions } from \"./ChangesetReaderTypes\";\r\nimport { AnyDb, SqliteChangeOp } from \"./SqliteChangesetReader\";\r\n\r\n\r\n// ---------------------------------------------------------------------------\r\n// ChangesetReader\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Reads EC-typed changeset data natively from a changeset file, changeset group,\r\n * in-memory transaction, or local un-pushed changes.\r\n *\r\n * Implements [ChangeSource]($backend) so rows can be fed directly into\r\n * [PartialChangeUnifier]($backend) to merge partial (per-table) instances into\r\n * complete EC instances.\r\n *\r\n * When the current row is a non-EC internal SQLite table, [[isECTable]] is `false`\r\n * and both [[inserted]] and [[deleted]] remain `undefined`.\r\n *\r\n * @note The native reader operates one SQLite table-row at a time. Multi-table EC\r\n * instances must be merged using [PartialChangeUnifier]($backend).\r\n * @beta\r\n */\r\nexport class ChangesetReader implements Disposable, ChangeSource {\r\n private static readonly defaultSpillThresholdInBytes = 50 * 1024 * 1024; // 50 MiB\r\n private readonly _nativeReader: IModelJsNative.ChangesetReader = new IModelNative.platform.ChangesetReader();\r\n // Internal options — keep ECClassId as raw Id so the unifier can use it as-is.\r\n private _rowOptions?: RowFormatOptions;\r\n private _batchSizeOverride?: number;\r\n private _propFilter: PropertyFilter = PropertyFilter.All;\r\n private _changeIndex = 0;\r\n /** Rows fetched in the most recent native batch call. */\r\n private _cache: IModelJsNative.ChangesetRowData[] = [];\r\n /**\r\n * Index of the current row in `_cache`.\r\n * Equals `_cache.length` (i.e. out-of-bounds) when no row is active:\r\n * initial state, after exhaustion, or after close().\r\n */\r\n private _cacheIndex = 0;\r\n /** Cached result of the `inserted` getter for the current row. `undefined` when not yet computed or not applicable. */\r\n private _cachedInserted: ChangeInstance | undefined = undefined;\r\n /** Cached result of the `deleted` getter for the current row. `undefined` when not yet computed or not applicable. */\r\n private _cachedDeleted: ChangeInstance | undefined = undefined;\r\n\r\n /** The db used for EC schema resolution. */\r\n public readonly db: AnyDb;\r\n\r\n /** Returns the active cached row, throwing if no row is current.\r\n * @internal */\r\n private get _currentRow(): IModelJsNative.ChangesetRowData {\r\n if (this._cacheIndex >= this._cache.length)\r\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader: no current row — call step() first.\");\r\n return this._cache[this._cacheIndex];\r\n }\r\n\r\n /** Returns the batch size to use for native step() calls based on the active property filter.\r\n * @internal */\r\n private get _batchSize(): number {\r\n if (this._batchSizeOverride !== undefined) return this._batchSizeOverride;\r\n if (this._propFilter === PropertyFilter.InstanceKey || this._propFilter === PropertyFilter.InstanceKeyAndIdentifiers) return 100;\r\n if (this._propFilter === PropertyFilter.BisCoreElement) return 20; // because BisCore Element class do not contain any GeomStream property so abbreviateBlobs is not relevant here\r\n if (this._rowOptions?.abbreviateBlobs === false) return 5;\r\n return 10; // PropertyFilter.All\r\n }\r\n\r\n /**\r\n * `true` when the current row belongs to an EC-mapped table.\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get isECTable(): boolean { return this._currentRow.metadata.isECTable; }\r\n\r\n /**\r\n * Name of the SQLite table for the current change row.\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get tableName(): string { return this._currentRow.metadata.tableName; }\r\n\r\n /**\r\n * `true` when the current change was applied indirectly\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get isIndirectChange(): boolean { return this._currentRow.metadata.isIndirectChange; }\r\n\r\n /**\r\n * Post-change (inserted or updated-new) EC instance, computed lazily after each [[step]] call.\r\n * `undefined` when the current row is a Delete or a non-EC table row or [[step]] returned false.\r\n * For UPDATE,inserted instances indicate the new state of the instance after the change has been applied and\r\n * deleted instances indicate the old state of the instance before the change has been applied.\r\n * For INSERT, inserted instances indicate the new state of the instance after the change has been applied and deleted instances are undefined.\r\n * For DELETE, deleted instances indicate the old state of the instance before the change has been applied and inserted instances are undefined.\r\n * @beta\r\n */\r\n public get inserted(): ChangeInstance | undefined {\r\n if (this._cachedInserted !== undefined) return this._cachedInserted;\r\n const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;\r\n if (!row || !row.newValues)\r\n return undefined;\r\n const op = this.op;\r\n return (this._cachedInserted = {\r\n ...row.newValues.data,\r\n $meta: {\r\n op,\r\n tables: [row.metadata.tableName],\r\n changeIndexes: [this._changeIndex],\r\n stage: \"New\",\r\n instanceKey: row.newValues.key,\r\n propFilter: this._propFilter,\r\n changeFetchedPropNames: row.newValues.changeFetchedPropNames,\r\n rowOptions: this._rowOptions,\r\n isIndirectChange: row.metadata.isIndirectChange,\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Pre-change (deleted or updated-old) EC instance, computed lazily after each [[step]] call.\r\n * `undefined` when the current row is an Insert or a non-EC table row or [[step]] returned false.\r\n * @beta\r\n */\r\n public get deleted(): ChangeInstance | undefined {\r\n if (this._cachedDeleted !== undefined) return this._cachedDeleted;\r\n const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;\r\n if (!row || !row.oldValues)\r\n return undefined;\r\n const op = this.op;\r\n return (this._cachedDeleted = {\r\n ...row.oldValues.data,\r\n $meta: {\r\n op,\r\n tables: [row.metadata.tableName],\r\n changeIndexes: [this._changeIndex],\r\n stage: \"Old\",\r\n instanceKey: row.oldValues.key,\r\n propFilter: this._propFilter,\r\n changeFetchedPropNames: row.oldValues.changeFetchedPropNames,\r\n rowOptions: this._rowOptions,\r\n isIndirectChange: row.metadata.isIndirectChange,\r\n },\r\n });\r\n }\r\n\r\n // Private — callers use static factory methods.\r\n private constructor(db: AnyDb) {\r\n this.db = db;\r\n }\r\n\r\n /** Map public RowFormatOptions to the native adaptor options.\r\n * @internal */\r\n private toNativeRowOptions(opts: RowFormatOptions): IModelJsNative.ECSqlRowAdaptorOptions {\r\n return {\r\n abbreviateBlobs: opts.abbreviateBlobs,\r\n classIdsToClassNames: opts.classIdsToClassNames,\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n useJsName: opts.useJsName,\r\n };\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Static factory methods\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Open a changeset file from disk.\r\n * @param args.fileName Absolute path to the changeset file.\r\n * @param args.db Database at or after the changeset's ending state, used for schema resolution.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @throws if the native layer fails to open the file.\r\n * @beta\r\n */\r\n public static openFile(args: { readonly fileName: string } & ChangesetReaderArgs): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openFile(args.db[_nativeDb], args.fileName, args.invert ?? false, reader._propFilter);\r\n }\r\n catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Concatenate multiple changeset files and read them as a single logical stream.\r\n * @param args.changesetFiles Ordered list of changeset file paths.\r\n * @param args.db Database with schema at or ahead of the last changeset.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of the changeset data in the change group exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for processing large changeset groups under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if `changesetFiles` is empty, or if the native layer fails to open\r\n * the group.\r\n * @beta\r\n */\r\n public static openGroup(args: { readonly changesetFiles: string[], spillThresholdInBytes?: number } & ChangesetReaderArgs): ChangesetReader {\r\n if (args.changesetFiles.length === 0)\r\n throw new IModelError(IModelStatus.BadArg, \"changesetFiles must contain at least one file.\");\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openGroup(args.db[_nativeDb], args.changesetFiles, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n }\r\n catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Read pending (not yet pushed) local changes from an open IModelDb.\r\n * @param args.db Must be an [IModelDb]($backend) (not [ECDb]($backend)).\r\n * @param args.includeInMemoryChanges Also include in-memory (not yet saved to disk) changes.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of all local un-pushed saved changes exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for iModels with large local change backlogs under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if the native layer\r\n * fails to open the local changes.\r\n * @beta\r\n */\r\n public static openLocalChanges(\r\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb; includeInMemoryChanges?: boolean, spillThresholdInBytes?: number },\r\n ): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openLocalChanges(args.db[_nativeDb], args.includeInMemoryChanges ?? false, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n } catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Read the in-memory (not yet saved to disk) changes of an open IModelDb.\r\n * @param args.db Must be an [IModelDb]($backend).\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of the in-memory (unsaved) change data exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for large in-memory transactions under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if the native layer encounters an error while opening the in-memory changes.\r\n * @beta\r\n */\r\n public static openInMemoryChanges(\r\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb, spillThresholdInBytes?: number },\r\n ): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openInMemoryChanges(args.db[_nativeDb], args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n } catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Read a single saved transaction by its id.\r\n * @param args.db Must be an [IModelDb]($backend) ([ECDb]($backend) does not support transactions).\r\n * @param args.txnId The id of the saved transaction to read.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of the transaction's change data exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for large transactions under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if `txnId` is not found, or\r\n * the native layer fails to open the transaction data.\r\n * @beta\r\n */\r\n public static openTxn(\r\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb; txnId: Id64String, spillThresholdInBytes?: number },\r\n ): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openTxn(args.db[_nativeDb], args.txnId, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n } catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /** Throws if [[step]] has already been called, preventing filter/mode changes mid-iteration.\r\n * @internal */\r\n private throwIfAlreadyStepped(): void {\r\n if (this._changeIndex > 0)\r\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader: filters and strict mode and batch size must be configured before the first call to step().\");\r\n }\r\n\r\n /** Handle errors that occur while auto closing the reader if there is also an error while opening the reader */\r\n private handleCloseErrorWhileOpening(e: unknown): void {\r\n try {\r\n this.close();\r\n } catch (closeError) {\r\n throw new IModelError(IModelStatus.BadArg, `Failed to open ChangesetReader with error ${e instanceof Error ? e.message : String(e)}.\r\n Additionally, that triggered an automatic closure of the reader\r\n releasing native resources which also failed with failure ${closeError instanceof Error ? closeError.message : String(closeError)}.\r\n Check native error logs for more details.`);\r\n }\r\n }\r\n\r\n /**\r\n * Set the number of rows to fetch and cache while stepping.\r\n * This is an advanced option that can be used to tune performance for large changesets.\r\n * Increasing the batch size improves throughput at the cost of higher peak memory; decreasing it keeps memory consumption lower.\r\n *\r\n * Default batch sizes when `setBatchSize` is not called:\r\n * - `InstanceKey` or `InstanceKeyAndIdentifiers` filter: **100**.\r\n * - `BisCoreElement` filter (any `abbreviateBlobs` setting): **20**.\r\n * - `All` filter, `abbreviateBlobs: false`: **5**.\r\n * - `All` filter (blobs abbreviated or unset): **10**.\r\n *\r\n * @param batchSize Number of rows to fetch and cache while stepping. Must be a positive integer.\r\n * @throws [[IModelError]] if [[step]] has already been called successfully, or if `batchSize` is not a positive integer.\r\n * @beta\r\n */\r\n public setBatchSize(batchSize: number): void {\r\n this.throwIfAlreadyStepped();\r\n if (!Number.isInteger(batchSize) || batchSize <= 0)\r\n throw new IModelError(IModelStatus.BadArg, \"ChangesetReader: batchSize must be a positive integer.\");\r\n this._batchSizeOverride = batchSize;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Filtering\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Restrict iteration to changes from the named SQLite tables.\r\n * That means the rows for changes from other tables will be skipped entirely and won't be visible through the reader.\r\n * @param tableNames SQLite table names to include.\r\n * Note: Table names must be provided in the correct case for proper filtering.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error while setting the filter.\r\n * @beta\r\n */\r\n public setTableNameFilters(tableNames: Set<string>): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.setTableNameFilters(Array.from(tableNames));\r\n }\r\n\r\n /**\r\n * Restrict iteration to changes with the given operation types.\r\n * That means the rows for changes with other operation types will be skipped entirely and won't be visible through the reader.\r\n * @param ops Operations to include.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error while setting the filter.\r\n * @beta\r\n */\r\n public setOpCodeFilters(ops: Set<SqliteChangeOp>): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.setOpCodeFilters(Array.from(ops));\r\n }\r\n\r\n /**\r\n * Restrict iteration to changes for the given EC class names.\r\n * That means the rows for changes from other EC classes will be skipped entirely and won't be visible through the reader.\r\n * @param classNames EC class names to include. The classNames should be in the full name format(i.e. \"SchemaName:ClassName\").\r\n * Note: Schema names and class names must be provided in the correct case for proper filtering. Derived classes are not automatically included, so they must be specified explicitly if needed.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error while setting the filter.\r\n * @beta\r\n */\r\n public setClassNameFilters(classNames: Set<string>): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.setClassNameFilters(Array.from(classNames));\r\n }\r\n\r\n /**\r\n * Remove the table-name filters\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public clearTableNameFilters(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.clearTableNameFilters();\r\n }\r\n\r\n /**\r\n * Remove the op-code filters\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public clearOpCodeFilters(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.clearOpCodeFilters();\r\n }\r\n\r\n /**\r\n * Remove the class-name filters\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public clearClassNameFilters(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.clearClassNameFilters();\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Strict mode\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Enable strict mode on the reader.\r\n *\r\n * Strict mode affects how the reader handles a **column-count mismatch** between a change\r\n * record and the corresponding live database table. Such a mismatch can occur when columns\r\n * have been added to a table after the changeset was created.\r\n *\r\n * When strict mode is **enabled**: if the number of columns recorded in a change row differs\r\n * from the number of columns currently present in the live table, the reader throws an error\r\n * instead of processing that row.\r\n *\r\n * Use strict mode when you need to be certain that every change row is interpreted against\r\n * exactly the schema that was in effect when the changeset was written.\r\n *\r\n * @see [[disableStrictMode]] — the default (lenient) behaviour.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public enableStrictMode(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.enableStrictMode();\r\n }\r\n\r\n /**\r\n * Disable strict mode on the reader (this is the default).\r\n *\r\n * When strict mode is **disabled**: if the number of columns recorded in a change row differs\r\n * from the number of columns currently present in the live table, the reader takes the\r\n * **minimum** of the two column counts and proceeds normally with that subset. This is safe\r\n * because SQLite only ever appends new columns at the end of a table and never removes them —\r\n * so older change records simply lack the trailing columns that were added later, and those\r\n * missing columns are silently ignored.\r\n *\r\n * @see [[enableStrictMode]] — throw on column-count mismatches instead.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public disableStrictMode(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.disableStrictMode();\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Iteration\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Advance to the next change.\r\n * @returns `true` while positioned on a valid change; `false` when the stream is exhausted.\r\n * @throws if the native layer encounters an error while reading or decoding\r\n * the next change.\r\n * @beta\r\n */\r\n public step(): boolean {\r\n this._cachedInserted = undefined;\r\n this._cachedDeleted = undefined;\r\n if (this._cacheIndex + 1 < this._cache.length) {\r\n // Still have rows in cache — advance the pointer\r\n this._cacheIndex++;\r\n } else {\r\n // Cache empty or fully consumed — fetch next batch from native\r\n const nativeRowOpts = this._rowOptions ? this.toNativeRowOptions(this._rowOptions) : {};\r\n this._cache = this._nativeReader.step(this._batchSize, nativeRowOpts);\r\n this._cacheIndex = 0;\r\n if (this._cache.length === 0) return false;\r\n }\r\n this._changeIndex++;\r\n return true;\r\n }\r\n\r\n /**\r\n * SQLite opcode of the current change.\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get op(): SqliteChangeOp {\r\n const opCode = this._currentRow.metadata.opCode;\r\n return opCode === DbOpcode.Insert ? \"Inserted\"\r\n : opCode === DbOpcode.Update ? \"Updated\"\r\n : \"Deleted\";\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Lifecycle\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Close the reader and release all native resources.\r\n *\r\n * @throws if the native layer encounters an error during cleanup. Native resources\r\n * are not fully released when this throws — check the native error\r\n * logs for details.\r\n * @beta\r\n */\r\n public close(): void {\r\n this._changeIndex = 0;\r\n this._cache = [];\r\n this._cacheIndex = 0;\r\n this._cachedInserted = undefined;\r\n this._cachedDeleted = undefined;\r\n this._nativeReader.close();\r\n }\r\n\r\n /**\r\n * Implements the `Disposable` contract — delegates to [[close]].\r\n *\r\n * @throws if the native layer fails to release its resources (re-thrown from [[close]]).\r\n * @beta\r\n */\r\n public [Symbol.dispose](): void {\r\n this.close();\r\n }\r\n}\r\n\r\n"]}
|