@itwin/core-backend 5.12.0-dev.5 → 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.
Files changed (92) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/lib/cjs/ChangeSummaryManager.d.ts.map +1 -1
  3. package/lib/cjs/ChangeSummaryManager.js +33 -37
  4. package/lib/cjs/ChangeSummaryManager.js.map +1 -1
  5. package/lib/cjs/ChangesetECAdaptor.d.ts.map +1 -1
  6. package/lib/cjs/ChangesetECAdaptor.js +4 -7
  7. package/lib/cjs/ChangesetECAdaptor.js.map +1 -1
  8. package/lib/cjs/ChangesetReader.d.ts +65 -23
  9. package/lib/cjs/ChangesetReader.d.ts.map +1 -1
  10. package/lib/cjs/ChangesetReader.js +162 -102
  11. package/lib/cjs/ChangesetReader.js.map +1 -1
  12. package/lib/cjs/ECDb.js +2 -2
  13. package/lib/cjs/ECDb.js.map +1 -1
  14. package/lib/cjs/ElementAspect.d.ts.map +1 -1
  15. package/lib/cjs/ElementAspect.js +6 -10
  16. package/lib/cjs/ElementAspect.js.map +1 -1
  17. package/lib/cjs/ElementTreeWalker.d.ts.map +1 -1
  18. package/lib/cjs/ElementTreeWalker.js +16 -24
  19. package/lib/cjs/ElementTreeWalker.js.map +1 -1
  20. package/lib/cjs/IModelDb.d.ts.map +1 -1
  21. package/lib/cjs/IModelDb.js +65 -85
  22. package/lib/cjs/IModelDb.js.map +1 -1
  23. package/lib/cjs/Relationship.d.ts.map +1 -1
  24. package/lib/cjs/Relationship.js +8 -11
  25. package/lib/cjs/Relationship.js.map +1 -1
  26. package/lib/cjs/ViewDefinition.d.ts.map +1 -1
  27. package/lib/cjs/ViewDefinition.js +8 -12
  28. package/lib/cjs/ViewDefinition.js.map +1 -1
  29. package/lib/cjs/ViewStore.d.ts.map +1 -1
  30. package/lib/cjs/ViewStore.js +6 -8
  31. package/lib/cjs/ViewStore.js.map +1 -1
  32. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  33. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js +8 -11
  34. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  35. package/lib/cjs/internal/ChannelAdmin.d.ts.map +1 -1
  36. package/lib/cjs/internal/ChannelAdmin.js +6 -10
  37. package/lib/cjs/internal/ChannelAdmin.js.map +1 -1
  38. package/lib/cjs/internal/annotations/fields.d.ts.map +1 -1
  39. package/lib/cjs/internal/annotations/fields.js +19 -43
  40. package/lib/cjs/internal/annotations/fields.js.map +1 -1
  41. package/lib/cjs/rpc-impl/IModelReadRpcImpl.d.ts.map +1 -1
  42. package/lib/cjs/rpc-impl/IModelReadRpcImpl.js +2 -3
  43. package/lib/cjs/rpc-impl/IModelReadRpcImpl.js.map +1 -1
  44. package/lib/esm/ChangeSummaryManager.d.ts.map +1 -1
  45. package/lib/esm/ChangeSummaryManager.js +34 -38
  46. package/lib/esm/ChangeSummaryManager.js.map +1 -1
  47. package/lib/esm/ChangesetECAdaptor.d.ts.map +1 -1
  48. package/lib/esm/ChangesetECAdaptor.js +5 -8
  49. package/lib/esm/ChangesetECAdaptor.js.map +1 -1
  50. package/lib/esm/ChangesetReader.d.ts +65 -23
  51. package/lib/esm/ChangesetReader.d.ts.map +1 -1
  52. package/lib/esm/ChangesetReader.js +163 -103
  53. package/lib/esm/ChangesetReader.js.map +1 -1
  54. package/lib/esm/ECDb.js +2 -2
  55. package/lib/esm/ECDb.js.map +1 -1
  56. package/lib/esm/ElementAspect.d.ts.map +1 -1
  57. package/lib/esm/ElementAspect.js +8 -12
  58. package/lib/esm/ElementAspect.js.map +1 -1
  59. package/lib/esm/ElementTreeWalker.d.ts.map +1 -1
  60. package/lib/esm/ElementTreeWalker.js +18 -26
  61. package/lib/esm/ElementTreeWalker.js.map +1 -1
  62. package/lib/esm/IModelDb.d.ts.map +1 -1
  63. package/lib/esm/IModelDb.js +66 -86
  64. package/lib/esm/IModelDb.js.map +1 -1
  65. package/lib/esm/Relationship.d.ts.map +1 -1
  66. package/lib/esm/Relationship.js +9 -12
  67. package/lib/esm/Relationship.js.map +1 -1
  68. package/lib/esm/ViewDefinition.d.ts.map +1 -1
  69. package/lib/esm/ViewDefinition.js +10 -14
  70. package/lib/esm/ViewDefinition.js.map +1 -1
  71. package/lib/esm/ViewStore.d.ts.map +1 -1
  72. package/lib/esm/ViewStore.js +7 -9
  73. package/lib/esm/ViewStore.js.map +1 -1
  74. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  75. package/lib/esm/annotations/ElementDrivesTextAnnotation.js +10 -13
  76. package/lib/esm/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  77. package/lib/esm/internal/ChannelAdmin.d.ts.map +1 -1
  78. package/lib/esm/internal/ChannelAdmin.js +7 -11
  79. package/lib/esm/internal/ChannelAdmin.js.map +1 -1
  80. package/lib/esm/internal/annotations/fields.d.ts.map +1 -1
  81. package/lib/esm/internal/annotations/fields.js +21 -45
  82. package/lib/esm/internal/annotations/fields.js.map +1 -1
  83. package/lib/esm/rpc-impl/IModelReadRpcImpl.d.ts.map +1 -1
  84. package/lib/esm/rpc-impl/IModelReadRpcImpl.js +2 -3
  85. package/lib/esm/rpc-impl/IModelReadRpcImpl.js.map +1 -1
  86. package/lib/esm/test/hubaccess/BriefcaseManager.test.js +1 -1
  87. package/lib/esm/test/hubaccess/BriefcaseManager.test.js.map +1 -1
  88. package/lib/esm/test/imodel/IModel.test.js +9 -3
  89. package/lib/esm/test/imodel/IModel.test.js.map +1 -1
  90. package/lib/esm/test/standalone/ChangesetReader.test.js +725 -110
  91. package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
  92. package/package.json +13 -13
@@ -36,59 +36,122 @@ class ChangesetReader {
36
36
  _nativeReader = new NativePlatform_1.IModelNative.platform.ChangesetReader();
37
37
  // Internal options — keep ECClassId as raw Id so the unifier can use it as-is.
38
38
  _rowOptions;
39
+ _batchSizeOverride;
39
40
  _propFilter = ChangesetReaderTypes_1.PropertyFilter.All;
40
41
  _changeIndex = 0;
41
- _op;
42
- _isECTable;
43
- _tableName;
44
- _isIndirectChange;
42
+ /** Rows fetched in the most recent native batch call. */
43
+ _cache = [];
44
+ /**
45
+ * Index of the current row in `_cache`.
46
+ * Equals `_cache.length` (i.e. out-of-bounds) when no row is active:
47
+ * initial state, after exhaustion, or after close().
48
+ */
49
+ _cacheIndex = 0;
50
+ /** Cached result of the `inserted` getter for the current row. `undefined` when not yet computed or not applicable. */
51
+ _cachedInserted = undefined;
52
+ /** Cached result of the `deleted` getter for the current row. `undefined` when not yet computed or not applicable. */
53
+ _cachedDeleted = undefined;
45
54
  /** The db used for EC schema resolution. */
46
55
  db;
56
+ /** Returns the active cached row, throwing if no row is current.
57
+ * @internal */
58
+ get _currentRow() {
59
+ if (this._cacheIndex >= this._cache.length)
60
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "ChangesetReader: no current row — call step() first.");
61
+ return this._cache[this._cacheIndex];
62
+ }
63
+ /** Returns the batch size to use for native step() calls based on the active property filter.
64
+ * @internal */
65
+ get _batchSize() {
66
+ if (this._batchSizeOverride !== undefined)
67
+ return this._batchSizeOverride;
68
+ if (this._propFilter === ChangesetReaderTypes_1.PropertyFilter.InstanceKey)
69
+ return 100;
70
+ if (this._propFilter === ChangesetReaderTypes_1.PropertyFilter.BisCoreElement)
71
+ return 20; // because BisCore Element class do not contain any GeomStream property so abbreviateBlobs is not relevant here
72
+ if (this._rowOptions?.abbreviateBlobs === false)
73
+ return 5;
74
+ return 10; // PropertyFilter.All
75
+ }
47
76
  /**
48
77
  * `true` when the current row belongs to an EC-mapped table.
49
78
  * Valid only after a successful call to [[step]].
50
79
  * @throws [[IModelError]] if called before a successful [[step]] call.
51
80
  * @beta
52
81
  */
53
- get isECTable() {
54
- if (this._isECTable === undefined)
55
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "ChangesetReader.isECTable is only valid after step() has positioned the reader on a current valid change.");
56
- return this._isECTable;
57
- }
82
+ get isECTable() { return this._currentRow.metadata.isECTable; }
58
83
  /**
59
84
  * Name of the SQLite table for the current change row.
60
85
  * Valid only after a successful call to [[step]].
61
86
  * @throws [[IModelError]] if called before a successful [[step]] call.
62
87
  * @beta
63
88
  */
64
- get tableName() {
65
- if (this._tableName === undefined)
66
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "ChangesetReader.tableName is only valid after step() has positioned the reader on a current valid change.");
67
- return this._tableName;
68
- }
89
+ get tableName() { return this._currentRow.metadata.tableName; }
69
90
  /**
70
91
  * `true` when the current change was applied indirectly
71
92
  * Valid only after a successful call to [[step]].
72
93
  * @throws [[IModelError]] if called before a successful [[step]] call.
73
94
  * @beta
74
95
  */
75
- get isIndirectChange() {
76
- if (this._isIndirectChange === undefined)
77
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "ChangesetReader.isIndirectChange is only valid after step() has positioned the reader on a current valid change.");
78
- return this._isIndirectChange;
79
- }
96
+ get isIndirectChange() { return this._currentRow.metadata.isIndirectChange; }
80
97
  /**
81
- * Post-change (inserted or updated-new) EC instance, populated after each [[step]] call.
98
+ * Post-change (inserted or updated-new) EC instance, computed lazily after each [[step]] call.
82
99
  * `undefined` when the current row is a Delete or a non-EC table row or [[step]] returned false.
100
+ * For UPDATE,inserted instances indicate the new state of the instance after the change has been applied and
101
+ * deleted instances indicate the old state of the instance before the change has been applied.
102
+ * For INSERT, inserted instances indicate the new state of the instance after the change has been applied and deleted instances are undefined.
103
+ * For DELETE, deleted instances indicate the old state of the instance before the change has been applied and inserted instances are undefined.
83
104
  * @beta
84
105
  */
85
- inserted;
106
+ get inserted() {
107
+ if (this._cachedInserted !== undefined)
108
+ return this._cachedInserted;
109
+ const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;
110
+ if (!row || !row.newValues)
111
+ return undefined;
112
+ const op = this.op;
113
+ return (this._cachedInserted = {
114
+ ...row.newValues.data,
115
+ $meta: {
116
+ op,
117
+ tables: [row.metadata.tableName],
118
+ changeIndexes: [this._changeIndex],
119
+ stage: "New",
120
+ instanceKey: row.newValues.key,
121
+ propFilter: this._propFilter,
122
+ changeFetchedPropNames: row.newValues.changeFetchedPropNames,
123
+ rowOptions: this._rowOptions,
124
+ isIndirectChange: row.metadata.isIndirectChange,
125
+ },
126
+ });
127
+ }
86
128
  /**
87
- * Pre-change (deleted or updated-old) EC instance, populated after each [[step]] call.
129
+ * Pre-change (deleted or updated-old) EC instance, computed lazily after each [[step]] call.
88
130
  * `undefined` when the current row is an Insert or a non-EC table row or [[step]] returned false.
89
131
  * @beta
90
132
  */
91
- deleted;
133
+ get deleted() {
134
+ if (this._cachedDeleted !== undefined)
135
+ return this._cachedDeleted;
136
+ const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;
137
+ if (!row || !row.oldValues)
138
+ return undefined;
139
+ const op = this.op;
140
+ return (this._cachedDeleted = {
141
+ ...row.oldValues.data,
142
+ $meta: {
143
+ op,
144
+ tables: [row.metadata.tableName],
145
+ changeIndexes: [this._changeIndex],
146
+ stage: "Old",
147
+ instanceKey: row.oldValues.key,
148
+ propFilter: this._propFilter,
149
+ changeFetchedPropNames: row.oldValues.changeFetchedPropNames,
150
+ rowOptions: this._rowOptions,
151
+ isIndirectChange: row.metadata.isIndirectChange,
152
+ },
153
+ });
154
+ }
92
155
  // Private — callers use static factory methods.
93
156
  constructor(db) {
94
157
  this.db = db;
@@ -109,8 +172,8 @@ class ChangesetReader {
109
172
  * Open a changeset file from disk.
110
173
  * @param args.fileName Absolute path to the changeset file.
111
174
  * @param args.db Database at or after the changeset's ending state, used for schema resolution.
112
- * @param args.invert When `true`, invert all operations (Insert↔Delete).
113
- * @param args.valueOptions Row adaptor options controlling how EC property values are formatted.
175
+ * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).
176
+ * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.
114
177
  * @param args.propFilter Controls which properties are included. Defaults to `All`.
115
178
  * @throws if the native layer fails to open the file.
116
179
  * @beta
@@ -133,7 +196,8 @@ class ChangesetReader {
133
196
  * Concatenate multiple changeset files and read them as a single logical stream.
134
197
  * @param args.changesetFiles Ordered list of changeset file paths.
135
198
  * @param args.db Database with schema at or ahead of the last changeset.
136
- * @param args.valueOptions Row adaptor options controlling how EC property values are formatted.
199
+ * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).
200
+ * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.
137
201
  * @param args.propFilter Controls which properties are included. Defaults to `All`.
138
202
  * @param args.spillThresholdInBytes When the total size of the changeset data in the change group exceeds this threshold (in bytes),
139
203
  * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.
@@ -163,7 +227,8 @@ class ChangesetReader {
163
227
  * Read pending (not yet pushed) local changes from an open IModelDb.
164
228
  * @param args.db Must be an [IModelDb]($backend) (not [ECDb]($backend)).
165
229
  * @param args.includeInMemoryChanges Also include in-memory (not yet saved to disk) changes.
166
- * @param args.valueOptions Row adaptor options controlling how EC property values are formatted.
230
+ * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).
231
+ * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.
167
232
  * @param args.propFilter Controls which properties are included. Defaults to `All`.
168
233
  * @param args.spillThresholdInBytes When the total size of all local un-pushed saved changes exceeds this threshold (in bytes),
169
234
  * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.
@@ -190,7 +255,8 @@ class ChangesetReader {
190
255
  /**
191
256
  * Read the in-memory (not yet saved to disk) changes of an open IModelDb.
192
257
  * @param args.db Must be an [IModelDb]($backend).
193
- * @param args.valueOptions Row adaptor options controlling how EC property values are formatted.
258
+ * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).
259
+ * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.
194
260
  * @param args.propFilter Controls which properties are included. Defaults to `All`.
195
261
  * @param args.spillThresholdInBytes When the total size of the in-memory (unsaved) change data exceeds this threshold (in bytes),
196
262
  * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.
@@ -217,7 +283,8 @@ class ChangesetReader {
217
283
  * Read a single saved transaction by its id.
218
284
  * @param args.db Must be an [IModelDb]($backend) ([ECDb]($backend) does not support transactions).
219
285
  * @param args.txnId The id of the saved transaction to read.
220
- * @param args.valueOptions Row adaptor options controlling how EC property values are formatted.
286
+ * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).
287
+ * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.
221
288
  * @param args.propFilter Controls which properties are included. Defaults to `All`.
222
289
  * @param args.spillThresholdInBytes When the total size of the transaction's change data exceeds this threshold (in bytes),
223
290
  * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.
@@ -241,6 +308,12 @@ class ChangesetReader {
241
308
  }
242
309
  return reader;
243
310
  }
311
+ /** Throws if [[step]] has already been called, preventing filter/mode changes mid-iteration.
312
+ * @internal */
313
+ throwIfAlreadyStepped() {
314
+ if (this._changeIndex > 0)
315
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "ChangesetReader: filters and strict mode and batch size must be configured before the first call to step().");
316
+ }
244
317
  /** Handle errors that occur while auto closing the reader if there is also an error while opening the reader */
245
318
  handleCloseErrorWhileOpening(e) {
246
319
  try {
@@ -253,6 +326,27 @@ class ChangesetReader {
253
326
  Check native error logs for more details.`);
254
327
  }
255
328
  }
329
+ /**
330
+ * Set the number of rows to fetch and cache while stepping.
331
+ * This is an advanced option that can be used to tune performance for large changesets.
332
+ * Increasing the batch size improves throughput at the cost of higher peak memory; decreasing it keeps memory consumption lower.
333
+ *
334
+ * Default batch sizes when `setBatchSize` is not called:
335
+ * - `InstanceKey` filter: **100**.
336
+ * - `BisCoreElement` filter (any `abbreviateBlobs` setting): **20**.
337
+ * - `All` filter, `abbreviateBlobs: false`: **5**.
338
+ * - `All` filter (blobs abbreviated or unset): **10**.
339
+ *
340
+ * @param batchSize Number of rows to fetch and cache while stepping. Must be a positive integer.
341
+ * @throws [[IModelError]] if [[step]] has already been called successfully, or if `batchSize` is not a positive integer.
342
+ * @beta
343
+ */
344
+ setBatchSize(batchSize) {
345
+ this.throwIfAlreadyStepped();
346
+ if (!Number.isInteger(batchSize) || batchSize <= 0)
347
+ throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadArg, "ChangesetReader: batchSize must be a positive integer.");
348
+ this._batchSizeOverride = batchSize;
349
+ }
256
350
  // ---------------------------------------------------------------------------
257
351
  // Filtering
258
352
  // ---------------------------------------------------------------------------
@@ -261,20 +355,22 @@ class ChangesetReader {
261
355
  * That means the rows for changes from other tables will be skipped entirely and won't be visible through the reader.
262
356
  * @param tableNames SQLite table names to include.
263
357
  * Note: Table names must be provided in the correct case for proper filtering.
264
- * @throws if the native layer encounters an error while setting the filter.
358
+ * @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.
265
359
  * @beta
266
360
  */
267
361
  setTableNameFilters(tableNames) {
362
+ this.throwIfAlreadyStepped();
268
363
  this._nativeReader.setTableNameFilters(Array.from(tableNames));
269
364
  }
270
365
  /**
271
366
  * Restrict iteration to changes with the given operation types.
272
367
  * That means the rows for changes with other operation types will be skipped entirely and won't be visible through the reader.
273
368
  * @param ops Operations to include.
274
- * @throws if the native layer encounters an error while setting the filter.
369
+ * @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.
275
370
  * @beta
276
371
  */
277
372
  setOpCodeFilters(ops) {
373
+ this.throwIfAlreadyStepped();
278
374
  this._nativeReader.setOpCodeFilters(Array.from(ops));
279
375
  }
280
376
  /**
@@ -282,34 +378,38 @@ class ChangesetReader {
282
378
  * That means the rows for changes from other EC classes will be skipped entirely and won't be visible through the reader.
283
379
  * @param classNames EC class names to include. The classNames should be in the full name format(i.e. "SchemaName:ClassName").
284
380
  * 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.
285
- * @throws if the native layer encounters an error while setting the filter.
381
+ * @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.
286
382
  * @beta
287
383
  */
288
384
  setClassNameFilters(classNames) {
385
+ this.throwIfAlreadyStepped();
289
386
  this._nativeReader.setClassNameFilters(Array.from(classNames));
290
387
  }
291
388
  /**
292
389
  * Remove the table-name filters
293
- * @throws if the native layer encounters an error.
390
+ * @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.
294
391
  * @beta
295
392
  */
296
393
  clearTableNameFilters() {
394
+ this.throwIfAlreadyStepped();
297
395
  this._nativeReader.clearTableNameFilters();
298
396
  }
299
397
  /**
300
398
  * Remove the op-code filters
301
- * @throws if the native layer encounters an error.
399
+ * @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.
302
400
  * @beta
303
401
  */
304
402
  clearOpCodeFilters() {
403
+ this.throwIfAlreadyStepped();
305
404
  this._nativeReader.clearOpCodeFilters();
306
405
  }
307
406
  /**
308
407
  * Remove the class-name filters
309
- * @throws if the native layer encounters an error.
408
+ * @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.
310
409
  * @beta
311
410
  */
312
411
  clearClassNameFilters() {
412
+ this.throwIfAlreadyStepped();
313
413
  this._nativeReader.clearClassNameFilters();
314
414
  }
315
415
  // ---------------------------------------------------------------------------
@@ -330,10 +430,11 @@ class ChangesetReader {
330
430
  * exactly the schema that was in effect when the changeset was written.
331
431
  *
332
432
  * @see [[disableStrictMode]] — the default (lenient) behaviour.
333
- * @throws if the native layer encounters an error.
433
+ * @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.
334
434
  * @beta
335
435
  */
336
436
  enableStrictMode() {
437
+ this.throwIfAlreadyStepped();
337
438
  this._nativeReader.enableStrictMode();
338
439
  }
339
440
  /**
@@ -347,80 +448,40 @@ class ChangesetReader {
347
448
  * missing columns are silently ignored.
348
449
  *
349
450
  * @see [[enableStrictMode]] — throw on column-count mismatches instead.
350
- * @throws if the native layer encounters an error.
451
+ * @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.
351
452
  * @beta
352
453
  */
353
454
  disableStrictMode() {
455
+ this.throwIfAlreadyStepped();
354
456
  this._nativeReader.disableStrictMode();
355
457
  }
356
458
  // ---------------------------------------------------------------------------
357
459
  // Iteration
358
460
  // ---------------------------------------------------------------------------
359
461
  /**
360
- * Advance to the next change and populate `inserted` and/or `deleted`.
462
+ * Advance to the next change.
361
463
  * @returns `true` while positioned on a valid change; `false` when the stream is exhausted.
362
464
  * @throws if the native layer encounters an error while reading or decoding
363
465
  * the next change.
364
466
  * @beta
365
467
  */
366
468
  step() {
367
- this.inserted = undefined;
368
- this.deleted = undefined;
369
- this._op = undefined;
370
- this._isECTable = undefined;
371
- this._tableName = undefined;
372
- this._isIndirectChange = undefined;
373
- if (this._nativeReader.step()) {
374
- this._changeIndex++;
375
- const meta = this._nativeReader.getChangeMetadata();
376
- const nativeOp = meta.opCode;
377
- const op = nativeOp === core_bentley_1.DbOpcode.Insert ? "Inserted" : nativeOp === core_bentley_1.DbOpcode.Update ? "Updated" : "Deleted";
378
- this._op = op;
379
- this._tableName = meta.tableName;
380
- this._isIndirectChange = meta.isIndirectChange;
381
- this._isECTable = meta.isECTable;
469
+ this._cachedInserted = undefined;
470
+ this._cachedDeleted = undefined;
471
+ if (this._cacheIndex + 1 < this._cache.length) {
472
+ // Still have rows in cache — advance the pointer
473
+ this._cacheIndex++;
474
+ }
475
+ else {
476
+ // Cache empty or fully consumed — fetch next batch from native
382
477
  const nativeRowOpts = this._rowOptions ? this.toNativeRowOptions(this._rowOptions) : {};
383
- if (op === "Inserted" || op === "Updated") {
384
- const rowValue = this._nativeReader.getValue(core_bentley_1.DbChangeStage.New, nativeRowOpts);
385
- if (rowValue !== undefined) {
386
- this.inserted = {
387
- ...rowValue.data,
388
- $meta: {
389
- op,
390
- tables: [this._tableName],
391
- changeIndexes: [this._changeIndex],
392
- stage: "New",
393
- instanceKey: rowValue.key,
394
- propFilter: this._propFilter,
395
- changeFetchedPropNames: rowValue.changeFetchedPropNames,
396
- rowOptions: this._rowOptions,
397
- isIndirectChange: this._isIndirectChange,
398
- },
399
- };
400
- }
401
- }
402
- if (op === "Deleted" || op === "Updated") {
403
- const rowValue = this._nativeReader.getValue(core_bentley_1.DbChangeStage.Old, nativeRowOpts);
404
- if (rowValue !== undefined) {
405
- this.deleted = {
406
- ...rowValue.data,
407
- $meta: {
408
- op,
409
- tables: [this._tableName],
410
- changeIndexes: [this._changeIndex],
411
- stage: "Old",
412
- instanceKey: rowValue.key,
413
- propFilter: this._propFilter,
414
- changeFetchedPropNames: rowValue.changeFetchedPropNames,
415
- rowOptions: this._rowOptions,
416
- isIndirectChange: this._isIndirectChange,
417
- },
418
- };
419
- }
420
- }
421
- return true;
478
+ this._cache = this._nativeReader.step(this._batchSize, nativeRowOpts);
479
+ this._cacheIndex = 0;
480
+ if (this._cache.length === 0)
481
+ return false;
422
482
  }
423
- return false;
483
+ this._changeIndex++;
484
+ return true;
424
485
  }
425
486
  /**
426
487
  * SQLite opcode of the current change.
@@ -429,9 +490,10 @@ class ChangesetReader {
429
490
  * @beta
430
491
  */
431
492
  get op() {
432
- if (this._op === undefined)
433
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "ChangesetReader.op is only valid after step() has positioned the reader on a current valid change.");
434
- return this._op;
493
+ const opCode = this._currentRow.metadata.opCode;
494
+ return opCode === core_bentley_1.DbOpcode.Insert ? "Inserted"
495
+ : opCode === core_bentley_1.DbOpcode.Update ? "Updated"
496
+ : "Deleted";
435
497
  }
436
498
  // ---------------------------------------------------------------------------
437
499
  // Lifecycle
@@ -446,12 +508,10 @@ class ChangesetReader {
446
508
  */
447
509
  close() {
448
510
  this._changeIndex = 0;
449
- this._op = undefined;
450
- this._isECTable = undefined;
451
- this._tableName = undefined;
452
- this._isIndirectChange = undefined;
453
- this.inserted = undefined;
454
- this.deleted = undefined;
511
+ this._cache = [];
512
+ this._cacheIndex = 0;
513
+ this._cachedInserted = undefined;
514
+ this._cachedDeleted = undefined;
455
515
  this._nativeReader.close();
456
516
  }
457
517
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ChangesetReader.js","sourceRoot":"","sources":["../../src/ChangesetReader.ts"],"names":[],"mappings":";;;AAAA;;;+FAG+F;AAC/F;;GAEG;AACH,sDAAwF;AACxF,oDAAiD;AAEjD,8DAAyD;AACzD,gDAA+C;AAE/C,iEAA6H;AAI7H,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E;;;;;;;;;;;;;;GAcG;AACH,MAAa,eAAe;IAClB,MAAM,CAAU,4BAA4B,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,SAAS;IACjE,aAAa,GAAmC,IAAI,6BAAY,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC;IAC7G,+EAA+E;IACvE,WAAW,CAAoB;IAC/B,WAAW,GAAmB,qCAAc,CAAC,GAAG,CAAC;IACjD,YAAY,GAAG,CAAC,CAAC;IACjB,GAAG,CAAkB;IACrB,UAAU,CAAW;IACrB,UAAU,CAAU;IACpB,iBAAiB,CAAW;IAEpC,4CAA4C;IAC5B,EAAE,CAAQ;IAE1B;;;;;OAKG;IACH,IAAW,SAAS;QAClB,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;YAC/B,MAAM,IAAI,yBAAW,CAAC,2BAAY,CAAC,UAAU,EAAE,2GAA2G,CAAC,CAAC;QAC9J,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACH,IAAW,SAAS;QAClB,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;YAC/B,MAAM,IAAI,yBAAW,CAAC,2BAAY,CAAC,UAAU,EAAE,2GAA2G,CAAC,CAAC;QAC9J,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACH,IAAW,gBAAgB;QACzB,IAAI,IAAI,CAAC,iBAAiB,KAAK,SAAS;YACtC,MAAM,IAAI,yBAAW,CAAC,2BAAY,CAAC,UAAU,EAAE,kHAAkH,CAAC,CAAC;QACrK,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAChC,CAAC;IAED;;;;OAIG;IACI,QAAQ,CAAkB;IAEjC;;;;OAIG;IACI,OAAO,CAAkB;IAEhC,gDAAgD;IAChD,YAAoB,EAAS;QAC3B,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;IAED;mBACe;IACP,kBAAkB,CAAC,IAAsB;QAC/C,OAAO;YACL,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;YAC/C,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,yBAAyB;IACzB,8EAA8E;IAE9E;;;;;;;;;OASG;IACI,MAAM,CAAC,QAAQ,CAAC,IAAyD;QAC9E,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,qCAAc,CAAC,GAAG,CAAC;QACzD,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,mBAAS,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;QAC7G,CAAC;QACD,OAAO,CAAC,EAAE,CAAC;YACT,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,CAAC,CAAC;QACV,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAC,SAAS,CAAC,IAAiG;QACvH,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC;YAClC,MAAM,IAAI,yBAAW,CAAC,2BAAY,CAAC,MAAM,EAAE,gDAAgD,CAAC,CAAC;QAC/F,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,qCAAc,CAAC,GAAG,CAAC;QACzD,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,mBAAS,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC,4BAA4B,CAAC,CAAC;QACrL,CAAC;QACD,OAAO,CAAC,EAAE,CAAC;YACT,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,CAAC,CAAC;QACV,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAC,gBAAgB,CAC5B,IAA0H;QAE1H,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,qCAAc,CAAC,GAAG,CAAC;QACzD,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,CAAC,aAAa,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,mBAAS,CAAC,EAAE,IAAI,CAAC,sBAAsB,IAAI,KAAK,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC7M,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,CAAC,CAAC;QACV,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;OAWG;IACI,MAAM,CAAC,mBAAmB,CAC/B,IAAwF;QAExF,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,qCAAc,CAAC,GAAG,CAAC;QACzD,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,CAAC,aAAa,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,mBAAS,CAAC,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC1K,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,CAAC,CAAC;QACV,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAC,OAAO,CACnB,IAA2G;QAE3G,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,qCAAc,CAAC,GAAG,CAAC;QACzD,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,mBAAS,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC1K,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,CAAC,CAAC;QACV,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,gHAAgH;IACxG,4BAA4B,CAAC,CAAU;QAC7C,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,MAAM,IAAI,yBAAW,CAAC,2BAAY,CAAC,MAAM,EAAE,6CAA6C,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;;kEAEtE,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;gDACvF,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,YAAY;IACZ,8EAA8E;IAE9E;;;;;;;OAOG;IACI,mBAAmB,CAAC,UAAuB;QAChD,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IACjE,CAAC;IAED;;;;;;OAMG;IACI,gBAAgB,CAAC,GAAwB;QAC9C,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACvD,CAAC;IAED;;;;;;;OAOG;IACI,mBAAmB,CAAC,UAAuB;QAChD,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IACjE,CAAC;IAED;;;;OAIG;IACI,qBAAqB;QAC1B,IAAI,CAAC,aAAa,CAAC,qBAAqB,EAAE,CAAC;IAC7C,CAAC;IAED;;;;OAIG;IACI,kBAAkB;QACvB,IAAI,CAAC,aAAa,CAAC,kBAAkB,EAAE,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACI,qBAAqB;QAC1B,IAAI,CAAC,aAAa,CAAC,qBAAqB,EAAE,CAAC;IAC7C,CAAC;IAED,8EAA8E;IAC9E,cAAc;IACd,8EAA8E;IAE9E;;;;;;;;;;;;;;;;;OAiBG;IACI,gBAAgB;QACrB,IAAI,CAAC,aAAa,CAAC,gBAAgB,EAAE,CAAC;IACxC,CAAC;IAED;;;;;;;;;;;;;OAaG;IACI,iBAAiB;QACtB,IAAI,CAAC,aAAa,CAAC,iBAAiB,EAAE,CAAC;IACzC,CAAC;IAED,8EAA8E;IAC9E,YAAY;IACZ,8EAA8E;IAE9E;;;;;;OAMG;IACI,IAAI;QACT,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,iBAAiB,GAAG,SAAS,CAAC;QAEnC,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9B,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,iBAAiB,EAAE,CAAC;YACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC;YAC7B,MAAM,EAAE,GAAmB,QAAQ,KAAK,uBAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,KAAK,uBAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;YAC5H,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;YAEd,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;YACjC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,CAAC;YAC/C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;YAEjC,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAExF,IAAI,EAAE,KAAK,UAAU,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;gBAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,4BAAa,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;gBAC/E,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAC3B,IAAI,CAAC,QAAQ,GAAG;wBACd,GAAG,QAAQ,CAAC,IAAI;wBAChB,KAAK,EAAE;4BACL,EAAE;4BACF,MAAM,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC;4BACzB,aAAa,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC;4BAClC,KAAK,EAAE,KAAK;4BACZ,WAAW,EAAE,QAAQ,CAAC,GAAG;4BACzB,UAAU,EAAE,IAAI,CAAC,WAAW;4BAC5B,sBAAsB,EAAE,QAAQ,CAAC,sBAAsB;4BACvD,UAAU,EAAE,IAAI,CAAC,WAAW;4BAC5B,gBAAgB,EAAE,IAAI,CAAC,iBAAiB;yBACzC;qBACF,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;gBACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,4BAAa,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;gBAC/E,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAC3B,IAAI,CAAC,OAAO,GAAG;wBACb,GAAG,QAAQ,CAAC,IAAI;wBAChB,KAAK,EAAE;4BACL,EAAE;4BACF,MAAM,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC;4BACzB,aAAa,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC;4BAClC,KAAK,EAAE,KAAK;4BACZ,WAAW,EAAE,QAAQ,CAAC,GAAG;4BACzB,UAAU,EAAE,IAAI,CAAC,WAAW;4BAC5B,sBAAsB,EAAE,QAAQ,CAAC,sBAAsB;4BACvD,UAAU,EAAE,IAAI,CAAC,WAAW;4BAC5B,gBAAgB,EAAE,IAAI,CAAC,iBAAiB;yBACzC;qBACF,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;OAKG;IACH,IAAW,EAAE;QACX,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS;YACxB,MAAM,IAAI,yBAAW,CAAC,2BAAY,CAAC,UAAU,EAAE,oGAAoG,CAAC,CAAC;QACvJ,OAAO,IAAI,CAAC,GAAG,CAAC;IAClB,CAAC;IAED,8EAA8E;IAC9E,YAAY;IACZ,8EAA8E;IAE9E;;;;;;;OAOG;IACI,KAAK;QACV,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,iBAAiB,GAAG,SAAS,CAAC;QACnC,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;IAED;;;;;OAKG;IACI,CAAC,MAAM,CAAC,OAAO,CAAC;QACrB,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;;AAvdH,0CAwdC","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/cjs/ECDb.js CHANGED
@@ -438,7 +438,7 @@ class ECDb {
438
438
  * */
439
439
  createQueryReader(ecsql, params, config) {
440
440
  if (!this._nativeDb || !this._nativeDb.isOpen()) {
441
- throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "db not open");
441
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR_NOTOPEN, "db not open");
442
442
  }
443
443
  const executor = {
444
444
  execute: async (request) => {
@@ -463,7 +463,7 @@ class ECDb {
463
463
  * */
464
464
  withQueryReader(ecsql, callback, params, config) {
465
465
  if (!this[Symbols_1._nativeDb].isOpen())
466
- throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "db not open");
466
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR_NOTOPEN, "db not open");
467
467
  const executor = new ECSqlRowExecutor_1.ECSqlRowExecutor(this);
468
468
  const reader = new ECSqlSyncReader_1.ECSqlSyncReader(executor, ecsql, params, config);
469
469
  const release = () => executor[Symbol.dispose]();