@itwin/core-backend 5.12.0-dev.6 → 5.12.0-dev.7
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 +9 -1
- package/lib/cjs/ChangesetReader.d.ts +65 -23
- package/lib/cjs/ChangesetReader.d.ts.map +1 -1
- package/lib/cjs/ChangesetReader.js +162 -102
- package/lib/cjs/ChangesetReader.js.map +1 -1
- package/lib/cjs/ECDb.js +2 -2
- package/lib/cjs/ECDb.js.map +1 -1
- package/lib/cjs/IModelDb.js +2 -2
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/esm/ChangesetReader.d.ts +65 -23
- package/lib/esm/ChangesetReader.d.ts.map +1 -1
- package/lib/esm/ChangesetReader.js +163 -103
- package/lib/esm/ChangesetReader.js.map +1 -1
- package/lib/esm/ECDb.js +2 -2
- package/lib/esm/ECDb.js.map +1 -1
- package/lib/esm/IModelDb.js +2 -2
- package/lib/esm/IModelDb.js.map +1 -1
- package/lib/esm/test/hubaccess/BriefcaseManager.test.js +1 -1
- package/lib/esm/test/hubaccess/BriefcaseManager.test.js.map +1 -1
- package/lib/esm/test/imodel/IModel.test.js +4 -4
- package/lib/esm/test/imodel/IModel.test.js.map +1 -1
- package/lib/esm/test/standalone/ChangesetReader.test.js +725 -110
- package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
- package/package.json +14 -14
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/** @packageDocumentation
|
|
6
6
|
* @module ECDb
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import { DbOpcode, IModelStatus } from "@itwin/core-bentley";
|
|
9
9
|
import { IModelError } from "@itwin/core-common";
|
|
10
10
|
import { IModelNative } from "./internal/NativePlatform";
|
|
11
11
|
import { _nativeDb } from "./internal/Symbols";
|
|
@@ -33,59 +33,122 @@ export class ChangesetReader {
|
|
|
33
33
|
_nativeReader = new IModelNative.platform.ChangesetReader();
|
|
34
34
|
// Internal options — keep ECClassId as raw Id so the unifier can use it as-is.
|
|
35
35
|
_rowOptions;
|
|
36
|
+
_batchSizeOverride;
|
|
36
37
|
_propFilter = PropertyFilter.All;
|
|
37
38
|
_changeIndex = 0;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
/** Rows fetched in the most recent native batch call. */
|
|
40
|
+
_cache = [];
|
|
41
|
+
/**
|
|
42
|
+
* Index of the current row in `_cache`.
|
|
43
|
+
* Equals `_cache.length` (i.e. out-of-bounds) when no row is active:
|
|
44
|
+
* initial state, after exhaustion, or after close().
|
|
45
|
+
*/
|
|
46
|
+
_cacheIndex = 0;
|
|
47
|
+
/** Cached result of the `inserted` getter for the current row. `undefined` when not yet computed or not applicable. */
|
|
48
|
+
_cachedInserted = undefined;
|
|
49
|
+
/** Cached result of the `deleted` getter for the current row. `undefined` when not yet computed or not applicable. */
|
|
50
|
+
_cachedDeleted = undefined;
|
|
42
51
|
/** The db used for EC schema resolution. */
|
|
43
52
|
db;
|
|
53
|
+
/** Returns the active cached row, throwing if no row is current.
|
|
54
|
+
* @internal */
|
|
55
|
+
get _currentRow() {
|
|
56
|
+
if (this._cacheIndex >= this._cache.length)
|
|
57
|
+
throw new IModelError(IModelStatus.BadRequest, "ChangesetReader: no current row — call step() first.");
|
|
58
|
+
return this._cache[this._cacheIndex];
|
|
59
|
+
}
|
|
60
|
+
/** Returns the batch size to use for native step() calls based on the active property filter.
|
|
61
|
+
* @internal */
|
|
62
|
+
get _batchSize() {
|
|
63
|
+
if (this._batchSizeOverride !== undefined)
|
|
64
|
+
return this._batchSizeOverride;
|
|
65
|
+
if (this._propFilter === PropertyFilter.InstanceKey)
|
|
66
|
+
return 100;
|
|
67
|
+
if (this._propFilter === PropertyFilter.BisCoreElement)
|
|
68
|
+
return 20; // because BisCore Element class do not contain any GeomStream property so abbreviateBlobs is not relevant here
|
|
69
|
+
if (this._rowOptions?.abbreviateBlobs === false)
|
|
70
|
+
return 5;
|
|
71
|
+
return 10; // PropertyFilter.All
|
|
72
|
+
}
|
|
44
73
|
/**
|
|
45
74
|
* `true` when the current row belongs to an EC-mapped table.
|
|
46
75
|
* Valid only after a successful call to [[step]].
|
|
47
76
|
* @throws [[IModelError]] if called before a successful [[step]] call.
|
|
48
77
|
* @beta
|
|
49
78
|
*/
|
|
50
|
-
get isECTable() {
|
|
51
|
-
if (this._isECTable === undefined)
|
|
52
|
-
throw new IModelError(IModelStatus.BadRequest, "ChangesetReader.isECTable is only valid after step() has positioned the reader on a current valid change.");
|
|
53
|
-
return this._isECTable;
|
|
54
|
-
}
|
|
79
|
+
get isECTable() { return this._currentRow.metadata.isECTable; }
|
|
55
80
|
/**
|
|
56
81
|
* Name of the SQLite table for the current change row.
|
|
57
82
|
* Valid only after a successful call to [[step]].
|
|
58
83
|
* @throws [[IModelError]] if called before a successful [[step]] call.
|
|
59
84
|
* @beta
|
|
60
85
|
*/
|
|
61
|
-
get tableName() {
|
|
62
|
-
if (this._tableName === undefined)
|
|
63
|
-
throw new IModelError(IModelStatus.BadRequest, "ChangesetReader.tableName is only valid after step() has positioned the reader on a current valid change.");
|
|
64
|
-
return this._tableName;
|
|
65
|
-
}
|
|
86
|
+
get tableName() { return this._currentRow.metadata.tableName; }
|
|
66
87
|
/**
|
|
67
88
|
* `true` when the current change was applied indirectly
|
|
68
89
|
* Valid only after a successful call to [[step]].
|
|
69
90
|
* @throws [[IModelError]] if called before a successful [[step]] call.
|
|
70
91
|
* @beta
|
|
71
92
|
*/
|
|
72
|
-
get isIndirectChange() {
|
|
73
|
-
if (this._isIndirectChange === undefined)
|
|
74
|
-
throw new IModelError(IModelStatus.BadRequest, "ChangesetReader.isIndirectChange is only valid after step() has positioned the reader on a current valid change.");
|
|
75
|
-
return this._isIndirectChange;
|
|
76
|
-
}
|
|
93
|
+
get isIndirectChange() { return this._currentRow.metadata.isIndirectChange; }
|
|
77
94
|
/**
|
|
78
|
-
* Post-change (inserted or updated-new) EC instance,
|
|
95
|
+
* Post-change (inserted or updated-new) EC instance, computed lazily after each [[step]] call.
|
|
79
96
|
* `undefined` when the current row is a Delete or a non-EC table row or [[step]] returned false.
|
|
97
|
+
* For UPDATE,inserted instances indicate the new state of the instance after the change has been applied and
|
|
98
|
+
* deleted instances indicate the old state of the instance before the change has been applied.
|
|
99
|
+
* For INSERT, inserted instances indicate the new state of the instance after the change has been applied and deleted instances are undefined.
|
|
100
|
+
* For DELETE, deleted instances indicate the old state of the instance before the change has been applied and inserted instances are undefined.
|
|
80
101
|
* @beta
|
|
81
102
|
*/
|
|
82
|
-
inserted
|
|
103
|
+
get inserted() {
|
|
104
|
+
if (this._cachedInserted !== undefined)
|
|
105
|
+
return this._cachedInserted;
|
|
106
|
+
const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;
|
|
107
|
+
if (!row || !row.newValues)
|
|
108
|
+
return undefined;
|
|
109
|
+
const op = this.op;
|
|
110
|
+
return (this._cachedInserted = {
|
|
111
|
+
...row.newValues.data,
|
|
112
|
+
$meta: {
|
|
113
|
+
op,
|
|
114
|
+
tables: [row.metadata.tableName],
|
|
115
|
+
changeIndexes: [this._changeIndex],
|
|
116
|
+
stage: "New",
|
|
117
|
+
instanceKey: row.newValues.key,
|
|
118
|
+
propFilter: this._propFilter,
|
|
119
|
+
changeFetchedPropNames: row.newValues.changeFetchedPropNames,
|
|
120
|
+
rowOptions: this._rowOptions,
|
|
121
|
+
isIndirectChange: row.metadata.isIndirectChange,
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
}
|
|
83
125
|
/**
|
|
84
|
-
* Pre-change (deleted or updated-old) EC instance,
|
|
126
|
+
* Pre-change (deleted or updated-old) EC instance, computed lazily after each [[step]] call.
|
|
85
127
|
* `undefined` when the current row is an Insert or a non-EC table row or [[step]] returned false.
|
|
86
128
|
* @beta
|
|
87
129
|
*/
|
|
88
|
-
deleted
|
|
130
|
+
get deleted() {
|
|
131
|
+
if (this._cachedDeleted !== undefined)
|
|
132
|
+
return this._cachedDeleted;
|
|
133
|
+
const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;
|
|
134
|
+
if (!row || !row.oldValues)
|
|
135
|
+
return undefined;
|
|
136
|
+
const op = this.op;
|
|
137
|
+
return (this._cachedDeleted = {
|
|
138
|
+
...row.oldValues.data,
|
|
139
|
+
$meta: {
|
|
140
|
+
op,
|
|
141
|
+
tables: [row.metadata.tableName],
|
|
142
|
+
changeIndexes: [this._changeIndex],
|
|
143
|
+
stage: "Old",
|
|
144
|
+
instanceKey: row.oldValues.key,
|
|
145
|
+
propFilter: this._propFilter,
|
|
146
|
+
changeFetchedPropNames: row.oldValues.changeFetchedPropNames,
|
|
147
|
+
rowOptions: this._rowOptions,
|
|
148
|
+
isIndirectChange: row.metadata.isIndirectChange,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
89
152
|
// Private — callers use static factory methods.
|
|
90
153
|
constructor(db) {
|
|
91
154
|
this.db = db;
|
|
@@ -106,8 +169,8 @@ export class ChangesetReader {
|
|
|
106
169
|
* Open a changeset file from disk.
|
|
107
170
|
* @param args.fileName Absolute path to the changeset file.
|
|
108
171
|
* @param args.db Database at or after the changeset's ending state, used for schema resolution.
|
|
109
|
-
* @param args.invert When `true`, invert all operations (Insert↔Delete).
|
|
110
|
-
* @param args.
|
|
172
|
+
* @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).
|
|
173
|
+
* @param args.rowOptions Row adaptor options controlling how EC property values are formatted.
|
|
111
174
|
* @param args.propFilter Controls which properties are included. Defaults to `All`.
|
|
112
175
|
* @throws if the native layer fails to open the file.
|
|
113
176
|
* @beta
|
|
@@ -130,7 +193,8 @@ export class ChangesetReader {
|
|
|
130
193
|
* Concatenate multiple changeset files and read them as a single logical stream.
|
|
131
194
|
* @param args.changesetFiles Ordered list of changeset file paths.
|
|
132
195
|
* @param args.db Database with schema at or ahead of the last changeset.
|
|
133
|
-
* @param args.
|
|
196
|
+
* @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).
|
|
197
|
+
* @param args.rowOptions Row adaptor options controlling how EC property values are formatted.
|
|
134
198
|
* @param args.propFilter Controls which properties are included. Defaults to `All`.
|
|
135
199
|
* @param args.spillThresholdInBytes When the total size of the changeset data in the change group exceeds this threshold (in bytes),
|
|
136
200
|
* the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.
|
|
@@ -160,7 +224,8 @@ export class ChangesetReader {
|
|
|
160
224
|
* Read pending (not yet pushed) local changes from an open IModelDb.
|
|
161
225
|
* @param args.db Must be an [IModelDb]($backend) (not [ECDb]($backend)).
|
|
162
226
|
* @param args.includeInMemoryChanges Also include in-memory (not yet saved to disk) changes.
|
|
163
|
-
* @param args.
|
|
227
|
+
* @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).
|
|
228
|
+
* @param args.rowOptions Row adaptor options controlling how EC property values are formatted.
|
|
164
229
|
* @param args.propFilter Controls which properties are included. Defaults to `All`.
|
|
165
230
|
* @param args.spillThresholdInBytes When the total size of all local un-pushed saved changes exceeds this threshold (in bytes),
|
|
166
231
|
* the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.
|
|
@@ -187,7 +252,8 @@ export class ChangesetReader {
|
|
|
187
252
|
/**
|
|
188
253
|
* Read the in-memory (not yet saved to disk) changes of an open IModelDb.
|
|
189
254
|
* @param args.db Must be an [IModelDb]($backend).
|
|
190
|
-
* @param args.
|
|
255
|
+
* @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).
|
|
256
|
+
* @param args.rowOptions Row adaptor options controlling how EC property values are formatted.
|
|
191
257
|
* @param args.propFilter Controls which properties are included. Defaults to `All`.
|
|
192
258
|
* @param args.spillThresholdInBytes When the total size of the in-memory (unsaved) change data exceeds this threshold (in bytes),
|
|
193
259
|
* the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.
|
|
@@ -214,7 +280,8 @@ export class ChangesetReader {
|
|
|
214
280
|
* Read a single saved transaction by its id.
|
|
215
281
|
* @param args.db Must be an [IModelDb]($backend) ([ECDb]($backend) does not support transactions).
|
|
216
282
|
* @param args.txnId The id of the saved transaction to read.
|
|
217
|
-
* @param args.
|
|
283
|
+
* @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).
|
|
284
|
+
* @param args.rowOptions Row adaptor options controlling how EC property values are formatted.
|
|
218
285
|
* @param args.propFilter Controls which properties are included. Defaults to `All`.
|
|
219
286
|
* @param args.spillThresholdInBytes When the total size of the transaction's change data exceeds this threshold (in bytes),
|
|
220
287
|
* the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.
|
|
@@ -238,6 +305,12 @@ export class ChangesetReader {
|
|
|
238
305
|
}
|
|
239
306
|
return reader;
|
|
240
307
|
}
|
|
308
|
+
/** Throws if [[step]] has already been called, preventing filter/mode changes mid-iteration.
|
|
309
|
+
* @internal */
|
|
310
|
+
throwIfAlreadyStepped() {
|
|
311
|
+
if (this._changeIndex > 0)
|
|
312
|
+
throw new IModelError(IModelStatus.BadRequest, "ChangesetReader: filters and strict mode and batch size must be configured before the first call to step().");
|
|
313
|
+
}
|
|
241
314
|
/** Handle errors that occur while auto closing the reader if there is also an error while opening the reader */
|
|
242
315
|
handleCloseErrorWhileOpening(e) {
|
|
243
316
|
try {
|
|
@@ -250,6 +323,27 @@ export class ChangesetReader {
|
|
|
250
323
|
Check native error logs for more details.`);
|
|
251
324
|
}
|
|
252
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Set the number of rows to fetch and cache while stepping.
|
|
328
|
+
* This is an advanced option that can be used to tune performance for large changesets.
|
|
329
|
+
* Increasing the batch size improves throughput at the cost of higher peak memory; decreasing it keeps memory consumption lower.
|
|
330
|
+
*
|
|
331
|
+
* Default batch sizes when `setBatchSize` is not called:
|
|
332
|
+
* - `InstanceKey` filter: **100**.
|
|
333
|
+
* - `BisCoreElement` filter (any `abbreviateBlobs` setting): **20**.
|
|
334
|
+
* - `All` filter, `abbreviateBlobs: false`: **5**.
|
|
335
|
+
* - `All` filter (blobs abbreviated or unset): **10**.
|
|
336
|
+
*
|
|
337
|
+
* @param batchSize Number of rows to fetch and cache while stepping. Must be a positive integer.
|
|
338
|
+
* @throws [[IModelError]] if [[step]] has already been called successfully, or if `batchSize` is not a positive integer.
|
|
339
|
+
* @beta
|
|
340
|
+
*/
|
|
341
|
+
setBatchSize(batchSize) {
|
|
342
|
+
this.throwIfAlreadyStepped();
|
|
343
|
+
if (!Number.isInteger(batchSize) || batchSize <= 0)
|
|
344
|
+
throw new IModelError(IModelStatus.BadArg, "ChangesetReader: batchSize must be a positive integer.");
|
|
345
|
+
this._batchSizeOverride = batchSize;
|
|
346
|
+
}
|
|
253
347
|
// ---------------------------------------------------------------------------
|
|
254
348
|
// Filtering
|
|
255
349
|
// ---------------------------------------------------------------------------
|
|
@@ -258,20 +352,22 @@ export class ChangesetReader {
|
|
|
258
352
|
* That means the rows for changes from other tables will be skipped entirely and won't be visible through the reader.
|
|
259
353
|
* @param tableNames SQLite table names to include.
|
|
260
354
|
* Note: Table names must be provided in the correct case for proper filtering.
|
|
261
|
-
* @throws if the native layer encounters an error while setting the filter.
|
|
355
|
+
* @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.
|
|
262
356
|
* @beta
|
|
263
357
|
*/
|
|
264
358
|
setTableNameFilters(tableNames) {
|
|
359
|
+
this.throwIfAlreadyStepped();
|
|
265
360
|
this._nativeReader.setTableNameFilters(Array.from(tableNames));
|
|
266
361
|
}
|
|
267
362
|
/**
|
|
268
363
|
* Restrict iteration to changes with the given operation types.
|
|
269
364
|
* That means the rows for changes with other operation types will be skipped entirely and won't be visible through the reader.
|
|
270
365
|
* @param ops Operations to include.
|
|
271
|
-
* @throws if the native layer encounters an error while setting the filter.
|
|
366
|
+
* @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.
|
|
272
367
|
* @beta
|
|
273
368
|
*/
|
|
274
369
|
setOpCodeFilters(ops) {
|
|
370
|
+
this.throwIfAlreadyStepped();
|
|
275
371
|
this._nativeReader.setOpCodeFilters(Array.from(ops));
|
|
276
372
|
}
|
|
277
373
|
/**
|
|
@@ -279,34 +375,38 @@ export class ChangesetReader {
|
|
|
279
375
|
* That means the rows for changes from other EC classes will be skipped entirely and won't be visible through the reader.
|
|
280
376
|
* @param classNames EC class names to include. The classNames should be in the full name format(i.e. "SchemaName:ClassName").
|
|
281
377
|
* 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.
|
|
282
|
-
* @throws if the native layer encounters an error while setting the filter.
|
|
378
|
+
* @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.
|
|
283
379
|
* @beta
|
|
284
380
|
*/
|
|
285
381
|
setClassNameFilters(classNames) {
|
|
382
|
+
this.throwIfAlreadyStepped();
|
|
286
383
|
this._nativeReader.setClassNameFilters(Array.from(classNames));
|
|
287
384
|
}
|
|
288
385
|
/**
|
|
289
386
|
* Remove the table-name filters
|
|
290
|
-
* @throws if the native layer encounters an error.
|
|
387
|
+
* @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.
|
|
291
388
|
* @beta
|
|
292
389
|
*/
|
|
293
390
|
clearTableNameFilters() {
|
|
391
|
+
this.throwIfAlreadyStepped();
|
|
294
392
|
this._nativeReader.clearTableNameFilters();
|
|
295
393
|
}
|
|
296
394
|
/**
|
|
297
395
|
* Remove the op-code filters
|
|
298
|
-
* @throws if the native layer encounters an error.
|
|
396
|
+
* @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.
|
|
299
397
|
* @beta
|
|
300
398
|
*/
|
|
301
399
|
clearOpCodeFilters() {
|
|
400
|
+
this.throwIfAlreadyStepped();
|
|
302
401
|
this._nativeReader.clearOpCodeFilters();
|
|
303
402
|
}
|
|
304
403
|
/**
|
|
305
404
|
* Remove the class-name filters
|
|
306
|
-
* @throws if the native layer encounters an error.
|
|
405
|
+
* @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.
|
|
307
406
|
* @beta
|
|
308
407
|
*/
|
|
309
408
|
clearClassNameFilters() {
|
|
409
|
+
this.throwIfAlreadyStepped();
|
|
310
410
|
this._nativeReader.clearClassNameFilters();
|
|
311
411
|
}
|
|
312
412
|
// ---------------------------------------------------------------------------
|
|
@@ -327,10 +427,11 @@ export class ChangesetReader {
|
|
|
327
427
|
* exactly the schema that was in effect when the changeset was written.
|
|
328
428
|
*
|
|
329
429
|
* @see [[disableStrictMode]] — the default (lenient) behaviour.
|
|
330
|
-
* @throws if the native layer encounters an error.
|
|
430
|
+
* @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.
|
|
331
431
|
* @beta
|
|
332
432
|
*/
|
|
333
433
|
enableStrictMode() {
|
|
434
|
+
this.throwIfAlreadyStepped();
|
|
334
435
|
this._nativeReader.enableStrictMode();
|
|
335
436
|
}
|
|
336
437
|
/**
|
|
@@ -344,80 +445,40 @@ export class ChangesetReader {
|
|
|
344
445
|
* missing columns are silently ignored.
|
|
345
446
|
*
|
|
346
447
|
* @see [[enableStrictMode]] — throw on column-count mismatches instead.
|
|
347
|
-
* @throws if the native layer encounters an error.
|
|
448
|
+
* @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.
|
|
348
449
|
* @beta
|
|
349
450
|
*/
|
|
350
451
|
disableStrictMode() {
|
|
452
|
+
this.throwIfAlreadyStepped();
|
|
351
453
|
this._nativeReader.disableStrictMode();
|
|
352
454
|
}
|
|
353
455
|
// ---------------------------------------------------------------------------
|
|
354
456
|
// Iteration
|
|
355
457
|
// ---------------------------------------------------------------------------
|
|
356
458
|
/**
|
|
357
|
-
* Advance to the next change
|
|
459
|
+
* Advance to the next change.
|
|
358
460
|
* @returns `true` while positioned on a valid change; `false` when the stream is exhausted.
|
|
359
461
|
* @throws if the native layer encounters an error while reading or decoding
|
|
360
462
|
* the next change.
|
|
361
463
|
* @beta
|
|
362
464
|
*/
|
|
363
465
|
step() {
|
|
364
|
-
this.
|
|
365
|
-
this.
|
|
366
|
-
this.
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
const meta = this._nativeReader.getChangeMetadata();
|
|
373
|
-
const nativeOp = meta.opCode;
|
|
374
|
-
const op = nativeOp === DbOpcode.Insert ? "Inserted" : nativeOp === DbOpcode.Update ? "Updated" : "Deleted";
|
|
375
|
-
this._op = op;
|
|
376
|
-
this._tableName = meta.tableName;
|
|
377
|
-
this._isIndirectChange = meta.isIndirectChange;
|
|
378
|
-
this._isECTable = meta.isECTable;
|
|
466
|
+
this._cachedInserted = undefined;
|
|
467
|
+
this._cachedDeleted = undefined;
|
|
468
|
+
if (this._cacheIndex + 1 < this._cache.length) {
|
|
469
|
+
// Still have rows in cache — advance the pointer
|
|
470
|
+
this._cacheIndex++;
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
// Cache empty or fully consumed — fetch next batch from native
|
|
379
474
|
const nativeRowOpts = this._rowOptions ? this.toNativeRowOptions(this._rowOptions) : {};
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
...rowValue.data,
|
|
385
|
-
$meta: {
|
|
386
|
-
op,
|
|
387
|
-
tables: [this._tableName],
|
|
388
|
-
changeIndexes: [this._changeIndex],
|
|
389
|
-
stage: "New",
|
|
390
|
-
instanceKey: rowValue.key,
|
|
391
|
-
propFilter: this._propFilter,
|
|
392
|
-
changeFetchedPropNames: rowValue.changeFetchedPropNames,
|
|
393
|
-
rowOptions: this._rowOptions,
|
|
394
|
-
isIndirectChange: this._isIndirectChange,
|
|
395
|
-
},
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
if (op === "Deleted" || op === "Updated") {
|
|
400
|
-
const rowValue = this._nativeReader.getValue(DbChangeStage.Old, nativeRowOpts);
|
|
401
|
-
if (rowValue !== undefined) {
|
|
402
|
-
this.deleted = {
|
|
403
|
-
...rowValue.data,
|
|
404
|
-
$meta: {
|
|
405
|
-
op,
|
|
406
|
-
tables: [this._tableName],
|
|
407
|
-
changeIndexes: [this._changeIndex],
|
|
408
|
-
stage: "Old",
|
|
409
|
-
instanceKey: rowValue.key,
|
|
410
|
-
propFilter: this._propFilter,
|
|
411
|
-
changeFetchedPropNames: rowValue.changeFetchedPropNames,
|
|
412
|
-
rowOptions: this._rowOptions,
|
|
413
|
-
isIndirectChange: this._isIndirectChange,
|
|
414
|
-
},
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
return true;
|
|
475
|
+
this._cache = this._nativeReader.step(this._batchSize, nativeRowOpts);
|
|
476
|
+
this._cacheIndex = 0;
|
|
477
|
+
if (this._cache.length === 0)
|
|
478
|
+
return false;
|
|
419
479
|
}
|
|
420
|
-
|
|
480
|
+
this._changeIndex++;
|
|
481
|
+
return true;
|
|
421
482
|
}
|
|
422
483
|
/**
|
|
423
484
|
* SQLite opcode of the current change.
|
|
@@ -426,9 +487,10 @@ export class ChangesetReader {
|
|
|
426
487
|
* @beta
|
|
427
488
|
*/
|
|
428
489
|
get op() {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
490
|
+
const opCode = this._currentRow.metadata.opCode;
|
|
491
|
+
return opCode === DbOpcode.Insert ? "Inserted"
|
|
492
|
+
: opCode === DbOpcode.Update ? "Updated"
|
|
493
|
+
: "Deleted";
|
|
432
494
|
}
|
|
433
495
|
// ---------------------------------------------------------------------------
|
|
434
496
|
// Lifecycle
|
|
@@ -443,12 +505,10 @@ export class ChangesetReader {
|
|
|
443
505
|
*/
|
|
444
506
|
close() {
|
|
445
507
|
this._changeIndex = 0;
|
|
446
|
-
this.
|
|
447
|
-
this.
|
|
448
|
-
this.
|
|
449
|
-
this.
|
|
450
|
-
this.inserted = undefined;
|
|
451
|
-
this.deleted = undefined;
|
|
508
|
+
this._cache = [];
|
|
509
|
+
this._cacheIndex = 0;
|
|
510
|
+
this._cachedInserted = undefined;
|
|
511
|
+
this._cachedDeleted = undefined;
|
|
452
512
|
this._nativeReader.close();
|
|
453
513
|
}
|
|
454
514
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChangesetReader.js","sourceRoot":"","sources":["../../src/ChangesetReader.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n* See LICENSE.md in the project root for license terms and full copyright notice.\n*--------------------------------------------------------------------------------------------*/\n/** @packageDocumentation\n * @module ECDb\n */\nimport { DbChangeStage, DbOpcode, Id64String, IModelStatus } from \"@itwin/core-bentley\";\nimport { IModelError } from \"@itwin/core-common\";\nimport { IModelDb } from \"./IModelDb\";\nimport { IModelNative } from \"./internal/NativePlatform\";\nimport { _nativeDb } from \"./internal/Symbols\";\nimport { IModelJsNative } from \"@bentley/imodeljs-native\";\nimport { ChangeInstance, ChangesetReaderArgs, ChangeSource, PropertyFilter, RowFormatOptions } from \"./ChangesetReaderTypes\";\nimport { AnyDb, SqliteChangeOp } from \"./SqliteChangesetReader\";\n\n\n// ---------------------------------------------------------------------------\n// ChangesetReader\n// ---------------------------------------------------------------------------\n\n/**\n * Reads EC-typed changeset data natively from a changeset file, changeset group,\n * in-memory transaction, or local un-pushed changes.\n *\n * Implements [ChangeSource]($backend) so rows can be fed directly into\n * [PartialChangeUnifier]($backend) to merge partial (per-table) instances into\n * complete EC instances.\n *\n * When the current row is a non-EC internal SQLite table, [[isECTable]] is `false`\n * and both [[inserted]] and [[deleted]] remain `undefined`.\n *\n * @note The native reader operates one SQLite table-row at a time. Multi-table EC\n * instances must be merged using [PartialChangeUnifier]($backend).\n * @beta\n */\nexport class ChangesetReader implements Disposable, ChangeSource {\n private static readonly defaultSpillThresholdInBytes = 50 * 1024 * 1024; // 50 MiB\n private readonly _nativeReader: IModelJsNative.ChangesetReader = new IModelNative.platform.ChangesetReader();\n // Internal options — keep ECClassId as raw Id so the unifier can use it as-is.\n private _rowOptions?: RowFormatOptions;\n private _propFilter: PropertyFilter = PropertyFilter.All;\n private _changeIndex = 0;\n private _op?: SqliteChangeOp;\n private _isECTable?: boolean;\n private _tableName?: string;\n private _isIndirectChange?: boolean;\n\n /** The db used for EC schema resolution. */\n public readonly db: AnyDb;\n\n /**\n * `true` when the current row belongs to an EC-mapped table.\n * Valid only after a successful call to [[step]].\n * @throws [[IModelError]] if called before a successful [[step]] call.\n * @beta\n */\n public get isECTable(): boolean {\n if (this._isECTable === undefined)\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader.isECTable is only valid after step() has positioned the reader on a current valid change.\");\n return this._isECTable;\n }\n\n /**\n * Name of the SQLite table for the current change row.\n * Valid only after a successful call to [[step]].\n * @throws [[IModelError]] if called before a successful [[step]] call.\n * @beta\n */\n public get tableName(): string {\n if (this._tableName === undefined)\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader.tableName is only valid after step() has positioned the reader on a current valid change.\");\n return this._tableName;\n }\n\n /**\n * `true` when the current change was applied indirectly\n * Valid only after a successful call to [[step]].\n * @throws [[IModelError]] if called before a successful [[step]] call.\n * @beta\n */\n public get isIndirectChange(): boolean {\n if (this._isIndirectChange === undefined)\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader.isIndirectChange is only valid after step() has positioned the reader on a current valid change.\");\n return this._isIndirectChange;\n }\n\n /**\n * Post-change (inserted or updated-new) EC instance, populated after each [[step]] call.\n * `undefined` when the current row is a Delete or a non-EC table row or [[step]] returned false.\n * @beta\n */\n public inserted?: ChangeInstance;\n\n /**\n * Pre-change (deleted or updated-old) EC instance, populated after each [[step]] call.\n * `undefined` when the current row is an Insert or a non-EC table row or [[step]] returned false.\n * @beta\n */\n public deleted?: ChangeInstance;\n\n // Private — callers use static factory methods.\n private constructor(db: AnyDb) {\n this.db = db;\n }\n\n /** Map public RowFormatOptions to the native adaptor options.\n * @internal */\n private toNativeRowOptions(opts: RowFormatOptions): IModelJsNative.ECSqlRowAdaptorOptions {\n return {\n abbreviateBlobs: opts.abbreviateBlobs,\n classIdsToClassNames: opts.classIdsToClassNames,\n useJsName: opts.useJsName,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Static factory methods\n // ---------------------------------------------------------------------------\n\n /**\n * Open a changeset file from disk.\n * @param args.fileName Absolute path to the changeset file.\n * @param args.db Database at or after the changeset's ending state, used for schema resolution.\n * @param args.invert When `true`, invert all operations (Insert↔Delete).\n * @param args.valueOptions Row adaptor options controlling how EC property values are formatted.\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\n * @throws if the native layer fails to open the file.\n * @beta\n */\n public static openFile(args: { readonly fileName: string } & ChangesetReaderArgs): ChangesetReader {\n const reader = new ChangesetReader(args.db);\n reader._rowOptions = args.rowOptions;\n const propFilter = args.propFilter ?? PropertyFilter.All;\n reader._propFilter = propFilter;\n try {\n reader._nativeReader.openFile(args.db[_nativeDb], args.fileName, args.invert ?? false, reader._propFilter);\n }\n catch (e) {\n reader.handleCloseErrorWhileOpening(e);\n throw e;\n }\n return reader;\n }\n\n /**\n * Concatenate multiple changeset files and read them as a single logical stream.\n * @param args.changesetFiles Ordered list of changeset file paths.\n * @param args.db Database with schema at or ahead of the last changeset.\n * @param args.valueOptions Row adaptor options controlling how EC property values are formatted.\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\n * @param args.spillThresholdInBytes When the total size of the changeset data in the change group exceeds this threshold (in bytes),\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\n * This keeps peak memory usage bounded, making the API suitable for processing large changeset groups under low-memory conditions.\n * Defaults to 50 MiB.\n * @throws if `changesetFiles` is empty, or if the native layer fails to open\n * the group.\n * @beta\n */\n public static openGroup(args: { readonly changesetFiles: string[], spillThresholdInBytes?: number } & ChangesetReaderArgs): ChangesetReader {\n if (args.changesetFiles.length === 0)\n throw new IModelError(IModelStatus.BadArg, \"changesetFiles must contain at least one file.\");\n const reader = new ChangesetReader(args.db);\n reader._rowOptions = args.rowOptions;\n const propFilter = args.propFilter ?? PropertyFilter.All;\n reader._propFilter = propFilter;\n try {\n reader._nativeReader.openGroup(args.db[_nativeDb], args.changesetFiles, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\n }\n catch (e) {\n reader.handleCloseErrorWhileOpening(e);\n throw e;\n }\n return reader;\n }\n\n /**\n * Read pending (not yet pushed) local changes from an open IModelDb.\n * @param args.db Must be an [IModelDb]($backend) (not [ECDb]($backend)).\n * @param args.includeInMemoryChanges Also include in-memory (not yet saved to disk) changes.\n * @param args.valueOptions Row adaptor options controlling how EC property values are formatted.\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\n * @param args.spillThresholdInBytes When the total size of all local un-pushed saved changes exceeds this threshold (in bytes),\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\n * This keeps peak memory usage bounded, making the API suitable for iModels with large local change backlogs under low-memory conditions.\n * Defaults to 50 MiB.\n * @throws if the native layer\n * fails to open the local changes.\n * @beta\n */\n public static openLocalChanges(\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb; includeInMemoryChanges?: boolean, spillThresholdInBytes?: number },\n ): ChangesetReader {\n const reader = new ChangesetReader(args.db);\n reader._rowOptions = args.rowOptions;\n const propFilter = args.propFilter ?? PropertyFilter.All;\n reader._propFilter = propFilter;\n try {\n reader._nativeReader.openLocalChanges(args.db[_nativeDb], args.includeInMemoryChanges ?? false, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\n } catch (e) {\n reader.handleCloseErrorWhileOpening(e);\n throw e;\n }\n return reader;\n }\n\n /**\n * Read the in-memory (not yet saved to disk) changes of an open IModelDb.\n * @param args.db Must be an [IModelDb]($backend).\n * @param args.valueOptions Row adaptor options controlling how EC property values are formatted.\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\n * @param args.spillThresholdInBytes When the total size of the in-memory (unsaved) change data exceeds this threshold (in bytes),\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\n * This keeps peak memory usage bounded, making the API suitable for large in-memory transactions under low-memory conditions.\n * Defaults to 50 MiB.\n * @throws if the native layer encounters an error while opening the in-memory changes.\n * @beta\n */\n public static openInMemoryChanges(\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb, spillThresholdInBytes?: number },\n ): ChangesetReader {\n const reader = new ChangesetReader(args.db);\n reader._rowOptions = args.rowOptions;\n const propFilter = args.propFilter ?? PropertyFilter.All;\n reader._propFilter = propFilter;\n try {\n reader._nativeReader.openInMemoryChanges(args.db[_nativeDb], args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\n } catch (e) {\n reader.handleCloseErrorWhileOpening(e);\n throw e;\n }\n return reader;\n }\n\n /**\n * Read a single saved transaction by its id.\n * @param args.db Must be an [IModelDb]($backend) ([ECDb]($backend) does not support transactions).\n * @param args.txnId The id of the saved transaction to read.\n * @param args.valueOptions Row adaptor options controlling how EC property values are formatted.\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\n * @param args.spillThresholdInBytes When the total size of the transaction's change data exceeds this threshold (in bytes),\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\n * This keeps peak memory usage bounded, making the API suitable for large transactions under low-memory conditions.\n * Defaults to 50 MiB.\n * @throws if `txnId` is not found, or\n * the native layer fails to open the transaction data.\n * @beta\n */\n public static openTxn(\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb; txnId: Id64String, spillThresholdInBytes?: number },\n ): ChangesetReader {\n const reader = new ChangesetReader(args.db);\n reader._rowOptions = args.rowOptions;\n const propFilter = args.propFilter ?? PropertyFilter.All;\n reader._propFilter = propFilter;\n try {\n reader._nativeReader.openTxn(args.db[_nativeDb], args.txnId, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\n } catch (e) {\n reader.handleCloseErrorWhileOpening(e);\n throw e;\n }\n return reader;\n }\n\n /** Handle errors that occur while auto closing the reader if there is also an error while opening the reader */\n private handleCloseErrorWhileOpening(e: unknown): void {\n try {\n this.close();\n } catch (closeError) {\n throw new IModelError(IModelStatus.BadArg, `Failed to open ChangesetReader with error ${e instanceof Error ? e.message : String(e)}.\n Additionally, that triggered an automatic closure of the reader\n releasing native resources which also failed with failure ${closeError instanceof Error ? closeError.message : String(closeError)}.\n Check native error logs for more details.`);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Filtering\n // ---------------------------------------------------------------------------\n\n /**\n * Restrict iteration to changes from the named SQLite tables.\n * That means the rows for changes from other tables will be skipped entirely and won't be visible through the reader.\n * @param tableNames SQLite table names to include.\n * Note: Table names must be provided in the correct case for proper filtering.\n * @throws if the native layer encounters an error while setting the filter.\n * @beta\n */\n public setTableNameFilters(tableNames: Set<string>): void {\n this._nativeReader.setTableNameFilters(Array.from(tableNames));\n }\n\n /**\n * Restrict iteration to changes with the given operation types.\n * That means the rows for changes with other operation types will be skipped entirely and won't be visible through the reader.\n * @param ops Operations to include.\n * @throws if the native layer encounters an error while setting the filter.\n * @beta\n */\n public setOpCodeFilters(ops: Set<SqliteChangeOp>): void {\n this._nativeReader.setOpCodeFilters(Array.from(ops));\n }\n\n /**\n * Restrict iteration to changes for the given EC class names.\n * That means the rows for changes from other EC classes will be skipped entirely and won't be visible through the reader.\n * @param classNames EC class names to include. The classNames should be in the full name format(i.e. \"SchemaName:ClassName\").\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.\n * @throws if the native layer encounters an error while setting the filter.\n * @beta\n */\n public setClassNameFilters(classNames: Set<string>): void {\n this._nativeReader.setClassNameFilters(Array.from(classNames));\n }\n\n /**\n * Remove the table-name filters\n * @throws if the native layer encounters an error.\n * @beta\n */\n public clearTableNameFilters(): void {\n this._nativeReader.clearTableNameFilters();\n }\n\n /**\n * Remove the op-code filters\n * @throws if the native layer encounters an error.\n * @beta\n */\n public clearOpCodeFilters(): void {\n this._nativeReader.clearOpCodeFilters();\n }\n\n /**\n * Remove the class-name filters\n * @throws if the native layer encounters an error.\n * @beta\n */\n public clearClassNameFilters(): void {\n this._nativeReader.clearClassNameFilters();\n }\n\n // ---------------------------------------------------------------------------\n // Strict mode\n // ---------------------------------------------------------------------------\n\n /**\n * Enable strict mode on the reader.\n *\n * Strict mode affects how the reader handles a **column-count mismatch** between a change\n * record and the corresponding live database table. Such a mismatch can occur when columns\n * have been added to a table after the changeset was created.\n *\n * When strict mode is **enabled**: if the number of columns recorded in a change row differs\n * from the number of columns currently present in the live table, the reader throws an error\n * instead of processing that row.\n *\n * Use strict mode when you need to be certain that every change row is interpreted against\n * exactly the schema that was in effect when the changeset was written.\n *\n * @see [[disableStrictMode]] — the default (lenient) behaviour.\n * @throws if the native layer encounters an error.\n * @beta\n */\n public enableStrictMode(): void {\n this._nativeReader.enableStrictMode();\n }\n\n /**\n * Disable strict mode on the reader (this is the default).\n *\n * When strict mode is **disabled**: if the number of columns recorded in a change row differs\n * from the number of columns currently present in the live table, the reader takes the\n * **minimum** of the two column counts and proceeds normally with that subset. This is safe\n * because SQLite only ever appends new columns at the end of a table and never removes them —\n * so older change records simply lack the trailing columns that were added later, and those\n * missing columns are silently ignored.\n *\n * @see [[enableStrictMode]] — throw on column-count mismatches instead.\n * @throws if the native layer encounters an error.\n * @beta\n */\n public disableStrictMode(): void {\n this._nativeReader.disableStrictMode();\n }\n\n // ---------------------------------------------------------------------------\n // Iteration\n // ---------------------------------------------------------------------------\n\n /**\n * Advance to the next change and populate `inserted` and/or `deleted`.\n * @returns `true` while positioned on a valid change; `false` when the stream is exhausted.\n * @throws if the native layer encounters an error while reading or decoding\n * the next change.\n * @beta\n */\n public step(): boolean {\n this.inserted = undefined;\n this.deleted = undefined;\n this._op = undefined;\n this._isECTable = undefined;\n this._tableName = undefined;\n this._isIndirectChange = undefined;\n\n if (this._nativeReader.step()) {\n this._changeIndex++;\n const meta = this._nativeReader.getChangeMetadata();\n const nativeOp = meta.opCode;\n const op: SqliteChangeOp = nativeOp === DbOpcode.Insert ? \"Inserted\" : nativeOp === DbOpcode.Update ? \"Updated\" : \"Deleted\";\n this._op = op;\n\n this._tableName = meta.tableName;\n this._isIndirectChange = meta.isIndirectChange;\n this._isECTable = meta.isECTable;\n\n const nativeRowOpts = this._rowOptions ? this.toNativeRowOptions(this._rowOptions) : {};\n\n if (op === \"Inserted\" || op === \"Updated\") {\n const rowValue = this._nativeReader.getValue(DbChangeStage.New, nativeRowOpts);\n if (rowValue !== undefined) {\n this.inserted = {\n ...rowValue.data,\n $meta: {\n op,\n tables: [this._tableName],\n changeIndexes: [this._changeIndex],\n stage: \"New\",\n instanceKey: rowValue.key,\n propFilter: this._propFilter,\n changeFetchedPropNames: rowValue.changeFetchedPropNames,\n rowOptions: this._rowOptions,\n isIndirectChange: this._isIndirectChange,\n },\n };\n }\n }\n\n if (op === \"Deleted\" || op === \"Updated\") {\n const rowValue = this._nativeReader.getValue(DbChangeStage.Old, nativeRowOpts);\n if (rowValue !== undefined) {\n this.deleted = {\n ...rowValue.data,\n $meta: {\n op,\n tables: [this._tableName],\n changeIndexes: [this._changeIndex],\n stage: \"Old\",\n instanceKey: rowValue.key,\n propFilter: this._propFilter,\n changeFetchedPropNames: rowValue.changeFetchedPropNames,\n rowOptions: this._rowOptions,\n isIndirectChange: this._isIndirectChange,\n },\n };\n }\n }\n\n return true;\n }\n\n return false;\n }\n\n /**\n * SQLite opcode of the current change.\n * Valid only after a successful call to [[step]].\n * @throws [[IModelError]] if called before a successful [[step]] call.\n * @beta\n */\n public get op(): SqliteChangeOp {\n if (this._op === undefined)\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader.op is only valid after step() has positioned the reader on a current valid change.\");\n return this._op;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n /**\n * Close the reader and release all native resources.\n *\n * @throws if the native layer encounters an error during cleanup. Native resources\n * are not fully released when this throws — check the native error\n * logs for details.\n * @beta\n */\n public close(): void {\n this._changeIndex = 0;\n this._op = undefined;\n this._isECTable = undefined;\n this._tableName = undefined;\n this._isIndirectChange = undefined;\n this.inserted = undefined;\n this.deleted = undefined;\n this._nativeReader.close();\n }\n\n /**\n * Implements the `Disposable` contract — delegates to [[close]].\n *\n * @throws if the native layer fails to release its resources (re-thrown from [[close]]).\n * @beta\n */\n public [Symbol.dispose](): void {\n this.close();\n }\n}\n\n\n"]}
|
|
1
|
+
{"version":3,"file":"ChangesetReader.js","sourceRoot":"","sources":["../../src/ChangesetReader.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n* See LICENSE.md in the project root for license terms and full copyright notice.\n*--------------------------------------------------------------------------------------------*/\n/** @packageDocumentation\n * @module ECDb\n */\nimport { DbOpcode, Id64String, IModelStatus } from \"@itwin/core-bentley\";\nimport { IModelError } from \"@itwin/core-common\";\nimport { IModelDb } from \"./IModelDb\";\nimport { IModelNative } from \"./internal/NativePlatform\";\nimport { _nativeDb } from \"./internal/Symbols\";\nimport { IModelJsNative } from \"@bentley/imodeljs-native\";\nimport { ChangeInstance, ChangesetReaderArgs, ChangeSource, PropertyFilter, RowFormatOptions } from \"./ChangesetReaderTypes\";\nimport { AnyDb, SqliteChangeOp } from \"./SqliteChangesetReader\";\n\n\n// ---------------------------------------------------------------------------\n// ChangesetReader\n// ---------------------------------------------------------------------------\n\n/**\n * Reads EC-typed changeset data natively from a changeset file, changeset group,\n * in-memory transaction, or local un-pushed changes.\n *\n * Implements [ChangeSource]($backend) so rows can be fed directly into\n * [PartialChangeUnifier]($backend) to merge partial (per-table) instances into\n * complete EC instances.\n *\n * When the current row is a non-EC internal SQLite table, [[isECTable]] is `false`\n * and both [[inserted]] and [[deleted]] remain `undefined`.\n *\n * @note The native reader operates one SQLite table-row at a time. Multi-table EC\n * instances must be merged using [PartialChangeUnifier]($backend).\n * @beta\n */\nexport class ChangesetReader implements Disposable, ChangeSource {\n private static readonly defaultSpillThresholdInBytes = 50 * 1024 * 1024; // 50 MiB\n private readonly _nativeReader: IModelJsNative.ChangesetReader = new IModelNative.platform.ChangesetReader();\n // Internal options — keep ECClassId as raw Id so the unifier can use it as-is.\n private _rowOptions?: RowFormatOptions;\n private _batchSizeOverride?: number;\n private _propFilter: PropertyFilter = PropertyFilter.All;\n private _changeIndex = 0;\n /** Rows fetched in the most recent native batch call. */\n private _cache: IModelJsNative.ChangesetRowData[] = [];\n /**\n * Index of the current row in `_cache`.\n * Equals `_cache.length` (i.e. out-of-bounds) when no row is active:\n * initial state, after exhaustion, or after close().\n */\n private _cacheIndex = 0;\n /** Cached result of the `inserted` getter for the current row. `undefined` when not yet computed or not applicable. */\n private _cachedInserted: ChangeInstance | undefined = undefined;\n /** Cached result of the `deleted` getter for the current row. `undefined` when not yet computed or not applicable. */\n private _cachedDeleted: ChangeInstance | undefined = undefined;\n\n /** The db used for EC schema resolution. */\n public readonly db: AnyDb;\n\n /** Returns the active cached row, throwing if no row is current.\n * @internal */\n private get _currentRow(): IModelJsNative.ChangesetRowData {\n if (this._cacheIndex >= this._cache.length)\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader: no current row — call step() first.\");\n return this._cache[this._cacheIndex];\n }\n\n /** Returns the batch size to use for native step() calls based on the active property filter.\n * @internal */\n private get _batchSize(): number {\n if (this._batchSizeOverride !== undefined) return this._batchSizeOverride;\n if (this._propFilter === PropertyFilter.InstanceKey) return 100;\n if (this._propFilter === PropertyFilter.BisCoreElement) return 20; // because BisCore Element class do not contain any GeomStream property so abbreviateBlobs is not relevant here\n if (this._rowOptions?.abbreviateBlobs === false) return 5;\n return 10; // PropertyFilter.All\n }\n\n /**\n * `true` when the current row belongs to an EC-mapped table.\n * Valid only after a successful call to [[step]].\n * @throws [[IModelError]] if called before a successful [[step]] call.\n * @beta\n */\n public get isECTable(): boolean { return this._currentRow.metadata.isECTable; }\n\n /**\n * Name of the SQLite table for the current change row.\n * Valid only after a successful call to [[step]].\n * @throws [[IModelError]] if called before a successful [[step]] call.\n * @beta\n */\n public get tableName(): string { return this._currentRow.metadata.tableName; }\n\n /**\n * `true` when the current change was applied indirectly\n * Valid only after a successful call to [[step]].\n * @throws [[IModelError]] if called before a successful [[step]] call.\n * @beta\n */\n public get isIndirectChange(): boolean { return this._currentRow.metadata.isIndirectChange; }\n\n /**\n * Post-change (inserted or updated-new) EC instance, computed lazily after each [[step]] call.\n * `undefined` when the current row is a Delete or a non-EC table row or [[step]] returned false.\n * For UPDATE,inserted instances indicate the new state of the instance after the change has been applied and\n * deleted instances indicate the old state of the instance before the change has been applied.\n * For INSERT, inserted instances indicate the new state of the instance after the change has been applied and deleted instances are undefined.\n * For DELETE, deleted instances indicate the old state of the instance before the change has been applied and inserted instances are undefined.\n * @beta\n */\n public get inserted(): ChangeInstance | undefined {\n if (this._cachedInserted !== undefined) return this._cachedInserted;\n const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;\n if (!row || !row.newValues)\n return undefined;\n const op = this.op;\n return (this._cachedInserted = {\n ...row.newValues.data,\n $meta: {\n op,\n tables: [row.metadata.tableName],\n changeIndexes: [this._changeIndex],\n stage: \"New\",\n instanceKey: row.newValues.key,\n propFilter: this._propFilter,\n changeFetchedPropNames: row.newValues.changeFetchedPropNames,\n rowOptions: this._rowOptions,\n isIndirectChange: row.metadata.isIndirectChange,\n },\n });\n }\n\n /**\n * Pre-change (deleted or updated-old) EC instance, computed lazily after each [[step]] call.\n * `undefined` when the current row is an Insert or a non-EC table row or [[step]] returned false.\n * @beta\n */\n public get deleted(): ChangeInstance | undefined {\n if (this._cachedDeleted !== undefined) return this._cachedDeleted;\n const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;\n if (!row || !row.oldValues)\n return undefined;\n const op = this.op;\n return (this._cachedDeleted = {\n ...row.oldValues.data,\n $meta: {\n op,\n tables: [row.metadata.tableName],\n changeIndexes: [this._changeIndex],\n stage: \"Old\",\n instanceKey: row.oldValues.key,\n propFilter: this._propFilter,\n changeFetchedPropNames: row.oldValues.changeFetchedPropNames,\n rowOptions: this._rowOptions,\n isIndirectChange: row.metadata.isIndirectChange,\n },\n });\n }\n\n // Private — callers use static factory methods.\n private constructor(db: AnyDb) {\n this.db = db;\n }\n\n /** Map public RowFormatOptions to the native adaptor options.\n * @internal */\n private toNativeRowOptions(opts: RowFormatOptions): IModelJsNative.ECSqlRowAdaptorOptions {\n return {\n abbreviateBlobs: opts.abbreviateBlobs,\n classIdsToClassNames: opts.classIdsToClassNames,\n useJsName: opts.useJsName,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Static factory methods\n // ---------------------------------------------------------------------------\n\n /**\n * Open a changeset file from disk.\n * @param args.fileName Absolute path to the changeset file.\n * @param args.db Database at or after the changeset's ending state, used for schema resolution.\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\n * @throws if the native layer fails to open the file.\n * @beta\n */\n public static openFile(args: { readonly fileName: string } & ChangesetReaderArgs): ChangesetReader {\n const reader = new ChangesetReader(args.db);\n reader._rowOptions = args.rowOptions;\n const propFilter = args.propFilter ?? PropertyFilter.All;\n reader._propFilter = propFilter;\n try {\n reader._nativeReader.openFile(args.db[_nativeDb], args.fileName, args.invert ?? false, reader._propFilter);\n }\n catch (e) {\n reader.handleCloseErrorWhileOpening(e);\n throw e;\n }\n return reader;\n }\n\n /**\n * Concatenate multiple changeset files and read them as a single logical stream.\n * @param args.changesetFiles Ordered list of changeset file paths.\n * @param args.db Database with schema at or ahead of the last changeset.\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\n * @param args.spillThresholdInBytes When the total size of the changeset data in the change group exceeds this threshold (in bytes),\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\n * This keeps peak memory usage bounded, making the API suitable for processing large changeset groups under low-memory conditions.\n * Defaults to 50 MiB.\n * @throws if `changesetFiles` is empty, or if the native layer fails to open\n * the group.\n * @beta\n */\n public static openGroup(args: { readonly changesetFiles: string[], spillThresholdInBytes?: number } & ChangesetReaderArgs): ChangesetReader {\n if (args.changesetFiles.length === 0)\n throw new IModelError(IModelStatus.BadArg, \"changesetFiles must contain at least one file.\");\n const reader = new ChangesetReader(args.db);\n reader._rowOptions = args.rowOptions;\n const propFilter = args.propFilter ?? PropertyFilter.All;\n reader._propFilter = propFilter;\n try {\n reader._nativeReader.openGroup(args.db[_nativeDb], args.changesetFiles, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\n }\n catch (e) {\n reader.handleCloseErrorWhileOpening(e);\n throw e;\n }\n return reader;\n }\n\n /**\n * Read pending (not yet pushed) local changes from an open IModelDb.\n * @param args.db Must be an [IModelDb]($backend) (not [ECDb]($backend)).\n * @param args.includeInMemoryChanges Also include in-memory (not yet saved to disk) changes.\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\n * @param args.spillThresholdInBytes When the total size of all local un-pushed saved changes exceeds this threshold (in bytes),\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\n * This keeps peak memory usage bounded, making the API suitable for iModels with large local change backlogs under low-memory conditions.\n * Defaults to 50 MiB.\n * @throws if the native layer\n * fails to open the local changes.\n * @beta\n */\n public static openLocalChanges(\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb; includeInMemoryChanges?: boolean, spillThresholdInBytes?: number },\n ): ChangesetReader {\n const reader = new ChangesetReader(args.db);\n reader._rowOptions = args.rowOptions;\n const propFilter = args.propFilter ?? PropertyFilter.All;\n reader._propFilter = propFilter;\n try {\n reader._nativeReader.openLocalChanges(args.db[_nativeDb], args.includeInMemoryChanges ?? false, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\n } catch (e) {\n reader.handleCloseErrorWhileOpening(e);\n throw e;\n }\n return reader;\n }\n\n /**\n * Read the in-memory (not yet saved to disk) changes of an open IModelDb.\n * @param args.db Must be an [IModelDb]($backend).\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\n * @param args.spillThresholdInBytes When the total size of the in-memory (unsaved) change data exceeds this threshold (in bytes),\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\n * This keeps peak memory usage bounded, making the API suitable for large in-memory transactions under low-memory conditions.\n * Defaults to 50 MiB.\n * @throws if the native layer encounters an error while opening the in-memory changes.\n * @beta\n */\n public static openInMemoryChanges(\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb, spillThresholdInBytes?: number },\n ): ChangesetReader {\n const reader = new ChangesetReader(args.db);\n reader._rowOptions = args.rowOptions;\n const propFilter = args.propFilter ?? PropertyFilter.All;\n reader._propFilter = propFilter;\n try {\n reader._nativeReader.openInMemoryChanges(args.db[_nativeDb], args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\n } catch (e) {\n reader.handleCloseErrorWhileOpening(e);\n throw e;\n }\n return reader;\n }\n\n /**\n * Read a single saved transaction by its id.\n * @param args.db Must be an [IModelDb]($backend) ([ECDb]($backend) does not support transactions).\n * @param args.txnId The id of the saved transaction to read.\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\n * @param args.spillThresholdInBytes When the total size of the transaction's change data exceeds this threshold (in bytes),\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\n * This keeps peak memory usage bounded, making the API suitable for large transactions under low-memory conditions.\n * Defaults to 50 MiB.\n * @throws if `txnId` is not found, or\n * the native layer fails to open the transaction data.\n * @beta\n */\n public static openTxn(\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb; txnId: Id64String, spillThresholdInBytes?: number },\n ): ChangesetReader {\n const reader = new ChangesetReader(args.db);\n reader._rowOptions = args.rowOptions;\n const propFilter = args.propFilter ?? PropertyFilter.All;\n reader._propFilter = propFilter;\n try {\n reader._nativeReader.openTxn(args.db[_nativeDb], args.txnId, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\n } catch (e) {\n reader.handleCloseErrorWhileOpening(e);\n throw e;\n }\n return reader;\n }\n\n /** Throws if [[step]] has already been called, preventing filter/mode changes mid-iteration.\n * @internal */\n private throwIfAlreadyStepped(): void {\n if (this._changeIndex > 0)\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader: filters and strict mode and batch size must be configured before the first call to step().\");\n }\n\n /** Handle errors that occur while auto closing the reader if there is also an error while opening the reader */\n private handleCloseErrorWhileOpening(e: unknown): void {\n try {\n this.close();\n } catch (closeError) {\n throw new IModelError(IModelStatus.BadArg, `Failed to open ChangesetReader with error ${e instanceof Error ? e.message : String(e)}.\n Additionally, that triggered an automatic closure of the reader\n releasing native resources which also failed with failure ${closeError instanceof Error ? closeError.message : String(closeError)}.\n Check native error logs for more details.`);\n }\n }\n\n /**\n * Set the number of rows to fetch and cache while stepping.\n * This is an advanced option that can be used to tune performance for large changesets.\n * Increasing the batch size improves throughput at the cost of higher peak memory; decreasing it keeps memory consumption lower.\n *\n * Default batch sizes when `setBatchSize` is not called:\n * - `InstanceKey` filter: **100**.\n * - `BisCoreElement` filter (any `abbreviateBlobs` setting): **20**.\n * - `All` filter, `abbreviateBlobs: false`: **5**.\n * - `All` filter (blobs abbreviated or unset): **10**.\n *\n * @param batchSize Number of rows to fetch and cache while stepping. Must be a positive integer.\n * @throws [[IModelError]] if [[step]] has already been called successfully, or if `batchSize` is not a positive integer.\n * @beta\n */\n public setBatchSize(batchSize: number): void {\n this.throwIfAlreadyStepped();\n if (!Number.isInteger(batchSize) || batchSize <= 0)\n throw new IModelError(IModelStatus.BadArg, \"ChangesetReader: batchSize must be a positive integer.\");\n this._batchSizeOverride = batchSize;\n }\n\n // ---------------------------------------------------------------------------\n // Filtering\n // ---------------------------------------------------------------------------\n\n /**\n * Restrict iteration to changes from the named SQLite tables.\n * That means the rows for changes from other tables will be skipped entirely and won't be visible through the reader.\n * @param tableNames SQLite table names to include.\n * Note: Table names must be provided in the correct case for proper filtering.\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.\n * @beta\n */\n public setTableNameFilters(tableNames: Set<string>): void {\n this.throwIfAlreadyStepped();\n this._nativeReader.setTableNameFilters(Array.from(tableNames));\n }\n\n /**\n * Restrict iteration to changes with the given operation types.\n * That means the rows for changes with other operation types will be skipped entirely and won't be visible through the reader.\n * @param ops Operations to include.\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.\n * @beta\n */\n public setOpCodeFilters(ops: Set<SqliteChangeOp>): void {\n this.throwIfAlreadyStepped();\n this._nativeReader.setOpCodeFilters(Array.from(ops));\n }\n\n /**\n * Restrict iteration to changes for the given EC class names.\n * That means the rows for changes from other EC classes will be skipped entirely and won't be visible through the reader.\n * @param classNames EC class names to include. The classNames should be in the full name format(i.e. \"SchemaName:ClassName\").\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.\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.\n * @beta\n */\n public setClassNameFilters(classNames: Set<string>): void {\n this.throwIfAlreadyStepped();\n this._nativeReader.setClassNameFilters(Array.from(classNames));\n }\n\n /**\n * Remove the table-name filters\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.\n * @beta\n */\n public clearTableNameFilters(): void {\n this.throwIfAlreadyStepped();\n this._nativeReader.clearTableNameFilters();\n }\n\n /**\n * Remove the op-code filters\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.\n * @beta\n */\n public clearOpCodeFilters(): void {\n this.throwIfAlreadyStepped();\n this._nativeReader.clearOpCodeFilters();\n }\n\n /**\n * Remove the class-name filters\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.\n * @beta\n */\n public clearClassNameFilters(): void {\n this.throwIfAlreadyStepped();\n this._nativeReader.clearClassNameFilters();\n }\n\n // ---------------------------------------------------------------------------\n // Strict mode\n // ---------------------------------------------------------------------------\n\n /**\n * Enable strict mode on the reader.\n *\n * Strict mode affects how the reader handles a **column-count mismatch** between a change\n * record and the corresponding live database table. Such a mismatch can occur when columns\n * have been added to a table after the changeset was created.\n *\n * When strict mode is **enabled**: if the number of columns recorded in a change row differs\n * from the number of columns currently present in the live table, the reader throws an error\n * instead of processing that row.\n *\n * Use strict mode when you need to be certain that every change row is interpreted against\n * exactly the schema that was in effect when the changeset was written.\n *\n * @see [[disableStrictMode]] — the default (lenient) behaviour.\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.\n * @beta\n */\n public enableStrictMode(): void {\n this.throwIfAlreadyStepped();\n this._nativeReader.enableStrictMode();\n }\n\n /**\n * Disable strict mode on the reader (this is the default).\n *\n * When strict mode is **disabled**: if the number of columns recorded in a change row differs\n * from the number of columns currently present in the live table, the reader takes the\n * **minimum** of the two column counts and proceeds normally with that subset. This is safe\n * because SQLite only ever appends new columns at the end of a table and never removes them —\n * so older change records simply lack the trailing columns that were added later, and those\n * missing columns are silently ignored.\n *\n * @see [[enableStrictMode]] — throw on column-count mismatches instead.\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.\n * @beta\n */\n public disableStrictMode(): void {\n this.throwIfAlreadyStepped();\n this._nativeReader.disableStrictMode();\n }\n\n // ---------------------------------------------------------------------------\n // Iteration\n // ---------------------------------------------------------------------------\n\n /**\n * Advance to the next change.\n * @returns `true` while positioned on a valid change; `false` when the stream is exhausted.\n * @throws if the native layer encounters an error while reading or decoding\n * the next change.\n * @beta\n */\n public step(): boolean {\n this._cachedInserted = undefined;\n this._cachedDeleted = undefined;\n if (this._cacheIndex + 1 < this._cache.length) {\n // Still have rows in cache — advance the pointer\n this._cacheIndex++;\n } else {\n // Cache empty or fully consumed — fetch next batch from native\n const nativeRowOpts = this._rowOptions ? this.toNativeRowOptions(this._rowOptions) : {};\n this._cache = this._nativeReader.step(this._batchSize, nativeRowOpts);\n this._cacheIndex = 0;\n if (this._cache.length === 0) return false;\n }\n this._changeIndex++;\n return true;\n }\n\n /**\n * SQLite opcode of the current change.\n * Valid only after a successful call to [[step]].\n * @throws [[IModelError]] if called before a successful [[step]] call.\n * @beta\n */\n public get op(): SqliteChangeOp {\n const opCode = this._currentRow.metadata.opCode;\n return opCode === DbOpcode.Insert ? \"Inserted\"\n : opCode === DbOpcode.Update ? \"Updated\"\n : \"Deleted\";\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n /**\n * Close the reader and release all native resources.\n *\n * @throws if the native layer encounters an error during cleanup. Native resources\n * are not fully released when this throws — check the native error\n * logs for details.\n * @beta\n */\n public close(): void {\n this._changeIndex = 0;\n this._cache = [];\n this._cacheIndex = 0;\n this._cachedInserted = undefined;\n this._cachedDeleted = undefined;\n this._nativeReader.close();\n }\n\n /**\n * Implements the `Disposable` contract — delegates to [[close]].\n *\n * @throws if the native layer fails to release its resources (re-thrown from [[close]]).\n * @beta\n */\n public [Symbol.dispose](): void {\n this.close();\n }\n}\n\n\n"]}
|
package/lib/esm/ECDb.js
CHANGED
|
@@ -435,7 +435,7 @@ export class ECDb {
|
|
|
435
435
|
* */
|
|
436
436
|
createQueryReader(ecsql, params, config) {
|
|
437
437
|
if (!this._nativeDb || !this._nativeDb.isOpen()) {
|
|
438
|
-
throw new IModelError(DbResult.
|
|
438
|
+
throw new IModelError(DbResult.BE_SQLITE_ERROR_NOTOPEN, "db not open");
|
|
439
439
|
}
|
|
440
440
|
const executor = {
|
|
441
441
|
execute: async (request) => {
|
|
@@ -460,7 +460,7 @@ export class ECDb {
|
|
|
460
460
|
* */
|
|
461
461
|
withQueryReader(ecsql, callback, params, config) {
|
|
462
462
|
if (!this[_nativeDb].isOpen())
|
|
463
|
-
throw new IModelError(DbResult.
|
|
463
|
+
throw new IModelError(DbResult.BE_SQLITE_ERROR_NOTOPEN, "db not open");
|
|
464
464
|
const executor = new ECSqlRowExecutor(this);
|
|
465
465
|
const reader = new ECSqlSyncReader(executor, ecsql, params, config);
|
|
466
466
|
const release = () => executor[Symbol.dispose]();
|