@itwin/core-backend 5.15.0-dev.2 → 5.15.0-dev.3
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 +18 -1
- package/lib/cjs/BriefcaseManager.d.ts +2 -2
- package/lib/cjs/BriefcaseManager.js.map +1 -1
- package/lib/cjs/ChangesetReaderTypes.d.ts +1 -1
- package/lib/cjs/ChangesetReaderTypes.js.map +1 -1
- package/lib/cjs/ECSqlRowExecutor.d.ts +3 -3
- package/lib/cjs/ECSqlRowExecutor.d.ts.map +1 -1
- package/lib/cjs/ECSqlRowExecutor.js +2 -2
- package/lib/cjs/ECSqlRowExecutor.js.map +1 -1
- package/lib/cjs/ECSqlStatement.d.ts +5 -0
- package/lib/cjs/ECSqlStatement.d.ts.map +1 -1
- package/lib/cjs/ECSqlStatement.js +15 -2
- package/lib/cjs/ECSqlStatement.js.map +1 -1
- package/lib/cjs/ECSqlSyncReader.d.ts.map +1 -1
- package/lib/cjs/ECSqlSyncReader.js +2 -2
- package/lib/cjs/ECSqlSyncReader.js.map +1 -1
- package/lib/esm/BriefcaseManager.d.ts +2 -2
- package/lib/esm/BriefcaseManager.js.map +1 -1
- package/lib/esm/ChangesetReaderTypes.d.ts +1 -1
- package/lib/esm/ChangesetReaderTypes.js.map +1 -1
- package/lib/esm/ECSqlRowExecutor.d.ts +3 -3
- package/lib/esm/ECSqlRowExecutor.d.ts.map +1 -1
- package/lib/esm/ECSqlRowExecutor.js +2 -2
- package/lib/esm/ECSqlRowExecutor.js.map +1 -1
- package/lib/esm/ECSqlStatement.d.ts +5 -0
- package/lib/esm/ECSqlStatement.d.ts.map +1 -1
- package/lib/esm/ECSqlStatement.js +15 -2
- package/lib/esm/ECSqlStatement.js.map +1 -1
- package/lib/esm/ECSqlSyncReader.d.ts.map +1 -1
- package/lib/esm/ECSqlSyncReader.js +3 -3
- package/lib/esm/ECSqlSyncReader.js.map +1 -1
- package/lib/esm/test/ecdb/ECSqlSyncReader.test.js +140 -0
- package/lib/esm/test/ecdb/ECSqlSyncReader.test.js.map +1 -1
- package/lib/esm/test/standalone/SessionApplyIndirect.test.d.ts +2 -0
- package/lib/esm/test/standalone/SessionApplyIndirect.test.d.ts.map +1 -0
- package/lib/esm/test/standalone/SessionApplyIndirect.test.js +211 -0
- package/lib/esm/test/standalone/SessionApplyIndirect.test.js.map +1 -0
- package/package.json +15 -15
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/** @packageDocumentation
|
|
6
6
|
* @module ECSQL
|
|
7
7
|
*/
|
|
8
|
-
import { DbValueFormat, ECSqlReaderBase, IModelError,
|
|
8
|
+
import { DbValueFormat, ECSqlReaderBase, IModelError, QueryBinder, QueryOptionsBuilder, QueryRowFormat, } from "@itwin/core-common";
|
|
9
9
|
import { DbResult } from "@itwin/core-bentley";
|
|
10
10
|
/**
|
|
11
11
|
* Execute ECSQL statements synchronously and read the results one row at a time.
|
|
@@ -86,7 +86,7 @@ export class ECSqlSyncReader extends ECSqlReaderBase {
|
|
|
86
86
|
}
|
|
87
87
|
// Fetch metadata once on the first call.
|
|
88
88
|
if (this._props.length === 0) {
|
|
89
|
-
this._props =
|
|
89
|
+
this._props = this._executor.fetchMetadata(this._cachedRowOptions);
|
|
90
90
|
}
|
|
91
91
|
return this.stepWithRetry();
|
|
92
92
|
}
|
|
@@ -115,7 +115,7 @@ export class ECSqlSyncReader extends ECSqlReaderBase {
|
|
|
115
115
|
*/
|
|
116
116
|
getMetaData() {
|
|
117
117
|
if (this._props.length === 0) {
|
|
118
|
-
this._props =
|
|
118
|
+
this._props = this._executor.fetchMetadata(this._cachedRowOptions);
|
|
119
119
|
}
|
|
120
120
|
return this._props.properties;
|
|
121
121
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ECSqlSyncReader.js","sourceRoot":"","sources":["../../src/ECSqlSyncReader.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module ECSQL\r\n */\r\nimport {\r\n DbValueFormat,\r\n ECSqlReaderBase,\r\n IModelError,\r\n PropertyMetaDataMap, QueryBinder, QueryOptions, QueryOptionsBuilder, QueryPropertyMetaData, QueryRowFormat, QueryRowProxy,\r\n} from \"@itwin/core-common\";\r\nimport { ECSqlRowExecutor } from \"./ECSqlRowExecutor\";\r\nimport { DbResult } from \"@itwin/core-bentley\";\r\n\r\n/** @beta */\r\nexport type SynchronousQueryOptions = Omit<QueryOptions, \"suppressLogErrors\" | \"includeMetaData\" | \"limit\" | \"priority\" | \"restartToken\" | \"delay\" | \"usePrimaryConn\" | \"quota\">;\r\n\r\n/**\r\n * Execute ECSQL statements synchronously and read the results one row at a time.\r\n *\r\n * This is the synchronous counterpart of [[ECSqlReader]] from `@itwin/core-common`.\r\n * It uses [[ECSqlRowExecutor]] directly — each call to [[step]] fetches exactly one row\r\n * with no internal caching, paging, or offset tracking.\r\n *\r\n * The query results are returned one row at a time. The format of the row is dictated by the\r\n * `rowFormat` specified in the `options` parameter of the constructed ECSqlSyncReader object.\r\n * Defaults to [[QueryRowFormat.UseECSqlPropertyIndexes]] when no `rowFormat` is defined.\r\n *\r\n * There are three primary ways to interact with and read the results:\r\n * - Iterate using ECSqlSyncReader as a synchronous iterator.\r\n * - Step manually using [[ECSqlSyncReader.step]].\r\n * - Capture all results at once using [[ECSqlSyncReader.toArray]].\r\n *\r\n * @note When iterating over the results, the current row is a [[QueryRowProxy]] object.\r\n * To get the row as a basic JavaScript object, call [[QueryRowProxy.toRow]] on it.\r\n * @beta\r\n */\r\nexport class ECSqlSyncReader extends ECSqlReaderBase implements IterableIterator<QueryRowProxy> {\r\n private _currentRow: any;\r\n private _options: SynchronousQueryOptions;\r\n /** Cached native row-adaptor options — built once and reused for every row. */\r\n private _cachedRowOptions: any;\r\n\r\n /**\r\n * @internal\r\n */\r\n public constructor(private _executor: ECSqlRowExecutor, public readonly query: string, param?: QueryBinder, options?: SynchronousQueryOptions) {\r\n const resolvedOptions = options ?? new QueryOptionsBuilder().getOptions();\r\n if (typeof resolvedOptions.rowFormat === \"undefined\")\r\n resolvedOptions.rowFormat = QueryRowFormat.UseECSqlPropertyIndexes;\r\n\r\n super(resolvedOptions.rowFormat);\r\n\r\n if (query.trim().length === 0) {\r\n throw new IModelError(DbResult.BE_SQLITE_ERROR, \"expecting non-empty ecsql statement\");\r\n }\r\n\r\n this._options = resolvedOptions;\r\n const serializedParam = param ? param.serialize() : new QueryBinder().serialize();\r\n\r\n // Prepare the statement and bind parameters once — avoids per-row ensureStatementReady overhead.\r\n const args = Object.keys(serializedParam).length > 0 ? serializedParam : undefined;\r\n this._executor.prepareAndBind(this.query, args);\r\n\r\n // Build and cache the native row-adaptor options — reused for every step call.\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n const valueFormat = this._options.rowFormat === QueryRowFormat.UseJsPropertyNames ? DbValueFormat.JsNames : DbValueFormat.ECSqlNames;\r\n this._cachedRowOptions = {\r\n abbreviateBlobs: this._options.abbreviateBlobs ?? false,\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n classIdsToClassNames: this._options.convertClassIdsToClassNames ?? false,\r\n useJsName: valueFormat === DbValueFormat.JsNames,\r\n doNotConvertClassIdsToClassNamesWhenAliased: true,\r\n };\r\n this._currentRow = undefined;\r\n }\r\n\r\n /**\r\n * @internal\r\n */\r\n protected override getRowInternal(): any[] {\r\n if (!this._currentRow)\r\n throw new IModelError(DbResult.BE_SQLITE_ERROR, \"no current row\");\r\n return this._currentRow as any[];\r\n }\r\n\r\n /**\r\n * Reads a single row from the executor. Returns the row data or `undefined` when the\r\n * result set is exhausted.\r\n *\r\n * Uses the fast-path `stepNextRow` method on the executor, which calls `stmt.step()`\r\n * and `stmt.toRow()` directly — no intermediate request/response objects are allocated per row.\r\n * @internal\r\n */\r\n private readRow(): any {\r\n if (this._done) {\r\n return undefined;\r\n }\r\n\r\n // Fetch metadata once on the first call.\r\n if (this._props.length === 0) {\r\n this._props = new PropertyMetaDataMap(this._executor.fetchMetadata(this._cachedRowOptions));\r\n }\r\n\r\n return this.stepWithRetry();\r\n }\r\n\r\n /**\r\n * Steps the cursor with retry logic for transient busy/interrupt conditions.\r\n * Returns the row data array, or `undefined` when the result set is exhausted.\r\n * @internal\r\n */\r\n private stepWithRetry(): any {\r\n const result = this._executor.stepNextRow(this._cachedRowOptions);\r\n if (result) {\r\n ECSqlSyncReader.replaceBase64WithUint8Array(result);\r\n return result;\r\n }\r\n return undefined; // undefined (done) → undefined\r\n }\r\n\r\n /**\r\n * Get the metadata for each column in the query result.\r\n *\r\n * If metadata has not been retrieved yet, fetches it directly from the prepared\r\n * statement. If a step has not yet occurred, executes a single step to ensure\r\n * the statement cursor is positioned, preserving the row for the next [[step]] call.\r\n *\r\n * @returns An array of [[QueryPropertyMetaData]].\r\n * @beta\r\n */\r\n public getMetaData(): QueryPropertyMetaData[] {\r\n if (this._props.length === 0) {\r\n this._props = new PropertyMetaDataMap(this._executor.fetchMetadata(this._cachedRowOptions));\r\n }\r\n return this._props.properties;\r\n }\r\n\r\n /**\r\n * Step to the next row of the query result.\r\n *\r\n * Each call executes exactly one step on the underlying statement — there is no\r\n * internal row caching or offset tracking.\r\n *\r\n * @returns `true` if a row can be read from `current`.<br/>\r\n * `false` if there are no more rows; i.e., all rows have been stepped through already.\r\n * @beta\r\n */\r\n public step(): boolean {\r\n if (this._done) {\r\n return false;\r\n }\r\n this._currentRow = this.readRow();\r\n if (!this._currentRow) {\r\n this._done = true;\r\n return false;\r\n }\r\n return true;\r\n }\r\n\r\n /**\r\n * Get all remaining rows from the query result.\r\n *\r\n * @returns An array of all remaining rows from the query result.\r\n * @beta\r\n */\r\n public toArray(): any[] {\r\n const rows = [];\r\n while (this.step()) {\r\n rows.push(this.formatCurrentRow());\r\n }\r\n return rows;\r\n }\r\n\r\n /**\r\n * Accessor for using ECSqlSyncReader as a synchronous iterator.\r\n *\r\n * @returns A synchronous iterator over the rows returned by the executed ECSQL query.\r\n * @beta\r\n */\r\n public [Symbol.iterator](): IterableIterator<QueryRowProxy> {\r\n return this;\r\n }\r\n\r\n /**\r\n * Calls step when called as an iterator.\r\n *\r\n * Returns the row alongside a `done` boolean to indicate if there are any more rows for an iterator to step to.\r\n *\r\n * @returns An object with the keys: `value` which contains the row and `done` which contains a boolean.\r\n * @beta\r\n */\r\n public next(): IteratorResult<QueryRowProxy, any> {\r\n if (this.step()) {\r\n return {\r\n done: false,\r\n value: this.current,\r\n };\r\n } else {\r\n return {\r\n done: true,\r\n value: this.current,\r\n };\r\n }\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"ECSqlSyncReader.js","sourceRoot":"","sources":["../../src/ECSqlSyncReader.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module ECSQL\r\n */\r\nimport {\r\n DbValueFormat,\r\n ECSqlReaderBase,\r\n IModelError,\r\n QueryBinder, QueryOptions, QueryOptionsBuilder, QueryPropertyMetaData, QueryRowFormat, QueryRowProxy,\r\n} from \"@itwin/core-common\";\r\nimport { ECSqlRowExecutor } from \"./ECSqlRowExecutor\";\r\nimport { DbResult } from \"@itwin/core-bentley\";\r\n\r\n/** @beta */\r\nexport type SynchronousQueryOptions = Omit<QueryOptions, \"suppressLogErrors\" | \"includeMetaData\" | \"limit\" | \"priority\" | \"restartToken\" | \"delay\" | \"usePrimaryConn\" | \"quota\">;\r\n\r\n/**\r\n * Execute ECSQL statements synchronously and read the results one row at a time.\r\n *\r\n * This is the synchronous counterpart of [[ECSqlReader]] from `@itwin/core-common`.\r\n * It uses [[ECSqlRowExecutor]] directly — each call to [[step]] fetches exactly one row\r\n * with no internal caching, paging, or offset tracking.\r\n *\r\n * The query results are returned one row at a time. The format of the row is dictated by the\r\n * `rowFormat` specified in the `options` parameter of the constructed ECSqlSyncReader object.\r\n * Defaults to [[QueryRowFormat.UseECSqlPropertyIndexes]] when no `rowFormat` is defined.\r\n *\r\n * There are three primary ways to interact with and read the results:\r\n * - Iterate using ECSqlSyncReader as a synchronous iterator.\r\n * - Step manually using [[ECSqlSyncReader.step]].\r\n * - Capture all results at once using [[ECSqlSyncReader.toArray]].\r\n *\r\n * @note When iterating over the results, the current row is a [[QueryRowProxy]] object.\r\n * To get the row as a basic JavaScript object, call [[QueryRowProxy.toRow]] on it.\r\n * @beta\r\n */\r\nexport class ECSqlSyncReader extends ECSqlReaderBase implements IterableIterator<QueryRowProxy> {\r\n private _currentRow: any;\r\n private _options: SynchronousQueryOptions;\r\n /** Cached native row-adaptor options — built once and reused for every row. */\r\n private _cachedRowOptions: any;\r\n\r\n /**\r\n * @internal\r\n */\r\n public constructor(private _executor: ECSqlRowExecutor, public readonly query: string, param?: QueryBinder, options?: SynchronousQueryOptions) {\r\n const resolvedOptions = options ?? new QueryOptionsBuilder().getOptions();\r\n if (typeof resolvedOptions.rowFormat === \"undefined\")\r\n resolvedOptions.rowFormat = QueryRowFormat.UseECSqlPropertyIndexes;\r\n\r\n super(resolvedOptions.rowFormat);\r\n\r\n if (query.trim().length === 0) {\r\n throw new IModelError(DbResult.BE_SQLITE_ERROR, \"expecting non-empty ecsql statement\");\r\n }\r\n\r\n this._options = resolvedOptions;\r\n const serializedParam = param ? param.serialize() : new QueryBinder().serialize();\r\n\r\n // Prepare the statement and bind parameters once — avoids per-row ensureStatementReady overhead.\r\n const args = Object.keys(serializedParam).length > 0 ? serializedParam : undefined;\r\n this._executor.prepareAndBind(this.query, args);\r\n\r\n // Build and cache the native row-adaptor options — reused for every step call.\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n const valueFormat = this._options.rowFormat === QueryRowFormat.UseJsPropertyNames ? DbValueFormat.JsNames : DbValueFormat.ECSqlNames;\r\n this._cachedRowOptions = {\r\n abbreviateBlobs: this._options.abbreviateBlobs ?? false,\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n classIdsToClassNames: this._options.convertClassIdsToClassNames ?? false,\r\n useJsName: valueFormat === DbValueFormat.JsNames,\r\n doNotConvertClassIdsToClassNamesWhenAliased: true,\r\n };\r\n this._currentRow = undefined;\r\n }\r\n\r\n /**\r\n * @internal\r\n */\r\n protected override getRowInternal(): any[] {\r\n if (!this._currentRow)\r\n throw new IModelError(DbResult.BE_SQLITE_ERROR, \"no current row\");\r\n return this._currentRow as any[];\r\n }\r\n\r\n /**\r\n * Reads a single row from the executor. Returns the row data or `undefined` when the\r\n * result set is exhausted.\r\n *\r\n * Uses the fast-path `stepNextRow` method on the executor, which calls `stmt.step()`\r\n * and `stmt.toRow()` directly — no intermediate request/response objects are allocated per row.\r\n * @internal\r\n */\r\n private readRow(): any {\r\n if (this._done) {\r\n return undefined;\r\n }\r\n\r\n // Fetch metadata once on the first call.\r\n if (this._props.length === 0) {\r\n this._props = this._executor.fetchMetadata(this._cachedRowOptions);\r\n }\r\n\r\n return this.stepWithRetry();\r\n }\r\n\r\n /**\r\n * Steps the cursor with retry logic for transient busy/interrupt conditions.\r\n * Returns the row data array, or `undefined` when the result set is exhausted.\r\n * @internal\r\n */\r\n private stepWithRetry(): any {\r\n const result = this._executor.stepNextRow(this._cachedRowOptions);\r\n if (result) {\r\n ECSqlSyncReader.replaceBase64WithUint8Array(result);\r\n return result;\r\n }\r\n return undefined; // undefined (done) → undefined\r\n }\r\n\r\n /**\r\n * Get the metadata for each column in the query result.\r\n *\r\n * If metadata has not been retrieved yet, fetches it directly from the prepared\r\n * statement. If a step has not yet occurred, executes a single step to ensure\r\n * the statement cursor is positioned, preserving the row for the next [[step]] call.\r\n *\r\n * @returns An array of [[QueryPropertyMetaData]].\r\n * @beta\r\n */\r\n public getMetaData(): QueryPropertyMetaData[] {\r\n if (this._props.length === 0) {\r\n this._props = this._executor.fetchMetadata(this._cachedRowOptions);\r\n }\r\n return this._props.properties;\r\n }\r\n\r\n /**\r\n * Step to the next row of the query result.\r\n *\r\n * Each call executes exactly one step on the underlying statement — there is no\r\n * internal row caching or offset tracking.\r\n *\r\n * @returns `true` if a row can be read from `current`.<br/>\r\n * `false` if there are no more rows; i.e., all rows have been stepped through already.\r\n * @beta\r\n */\r\n public step(): boolean {\r\n if (this._done) {\r\n return false;\r\n }\r\n this._currentRow = this.readRow();\r\n if (!this._currentRow) {\r\n this._done = true;\r\n return false;\r\n }\r\n return true;\r\n }\r\n\r\n /**\r\n * Get all remaining rows from the query result.\r\n *\r\n * @returns An array of all remaining rows from the query result.\r\n * @beta\r\n */\r\n public toArray(): any[] {\r\n const rows = [];\r\n while (this.step()) {\r\n rows.push(this.formatCurrentRow());\r\n }\r\n return rows;\r\n }\r\n\r\n /**\r\n * Accessor for using ECSqlSyncReader as a synchronous iterator.\r\n *\r\n * @returns A synchronous iterator over the rows returned by the executed ECSQL query.\r\n * @beta\r\n */\r\n public [Symbol.iterator](): IterableIterator<QueryRowProxy> {\r\n return this;\r\n }\r\n\r\n /**\r\n * Calls step when called as an iterator.\r\n *\r\n * Returns the row alongside a `done` boolean to indicate if there are any more rows for an iterator to step to.\r\n *\r\n * @returns An object with the keys: `value` which contains the row and `done` which contains a boolean.\r\n * @beta\r\n */\r\n public next(): IteratorResult<QueryRowProxy, any> {\r\n if (this.step()) {\r\n return {\r\n done: false,\r\n value: this.current,\r\n };\r\n } else {\r\n return {\r\n done: true,\r\n value: this.current,\r\n };\r\n }\r\n }\r\n}\r\n"]}
|
|
@@ -56,6 +56,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
56
56
|
});
|
|
57
57
|
import { assert, expect } from "chai";
|
|
58
58
|
import * as sinon from "sinon";
|
|
59
|
+
import * as path from "node:path";
|
|
59
60
|
import { QueryBinder, QueryOptionsBuilder, QueryRowFormat } from "@itwin/core-common";
|
|
60
61
|
import { SnapshotDb } from "../../IModelDb";
|
|
61
62
|
import { IModelTestUtils } from "../IModelTestUtils";
|
|
@@ -63,6 +64,8 @@ import { Id64 } from "@itwin/core-bentley";
|
|
|
63
64
|
import { ECSqlStatement } from "../../ECSqlStatement";
|
|
64
65
|
import { ECDbTestHelper } from "./ECDbTestHelper";
|
|
65
66
|
import { KnownTestLocations } from "../KnownTestLocations";
|
|
67
|
+
import { IModelNative } from "../../internal/NativePlatform";
|
|
68
|
+
import { IModelJsFs } from "../../IModelJsFs";
|
|
66
69
|
describe("WithQueryReaderTests", () => {
|
|
67
70
|
let iModel;
|
|
68
71
|
before(async () => {
|
|
@@ -257,5 +260,142 @@ describe("WithQueryReaderTests", () => {
|
|
|
257
260
|
}
|
|
258
261
|
});
|
|
259
262
|
});
|
|
263
|
+
describe("metadata caching", () => {
|
|
264
|
+
// Column metadata depends only on the prepared statement and the row-adaptor options, so it is cached
|
|
265
|
+
// on the cached ECSqlStatement instead of being fetched from native code on every withQueryReader call.
|
|
266
|
+
const sql = "SELECT ECInstanceId, ECClassId, CodeValue, GeometryStream FROM bis.GeometricElement3d WHERE ECInstanceId=?";
|
|
267
|
+
let elementId;
|
|
268
|
+
before(() => {
|
|
269
|
+
elementId = iModel.withQueryReader("SELECT ECInstanceId FROM bis.GeometricElement3d WHERE GeometryStream IS NOT NULL LIMIT 1", (reader) => {
|
|
270
|
+
assert.isTrue(reader.step());
|
|
271
|
+
return reader.current[0];
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
afterEach(() => sinon.restore());
|
|
275
|
+
function readMetadata(options) {
|
|
276
|
+
return iModel.withQueryReader(sql, (reader) => {
|
|
277
|
+
assert.isTrue(reader.step());
|
|
278
|
+
return { meta: reader.getMetaData(), row: reader.current.toRow() };
|
|
279
|
+
}, new QueryBinder().bindId(1, elementId), options);
|
|
280
|
+
}
|
|
281
|
+
it("returns metadata matching the row options when the same statement is reused with different options", () => {
|
|
282
|
+
const optionSets = [
|
|
283
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
284
|
+
{ rowFormat: QueryRowFormat.UseJsPropertyNames },
|
|
285
|
+
{ rowFormat: QueryRowFormat.UseECSqlPropertyNames },
|
|
286
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
287
|
+
{ rowFormat: QueryRowFormat.UseJsPropertyNames, abbreviateBlobs: true },
|
|
288
|
+
{ rowFormat: QueryRowFormat.UseECSqlPropertyNames, abbreviateBlobs: true },
|
|
289
|
+
{ rowFormat: QueryRowFormat.UseECSqlPropertyNames, convertClassIdsToClassNames: true },
|
|
290
|
+
];
|
|
291
|
+
// Expected metadata comes from a freshly prepared statement for each option set.
|
|
292
|
+
const expected = optionSets.map((options) => {
|
|
293
|
+
iModel.clearCaches();
|
|
294
|
+
return readMetadata({ ...options }).meta;
|
|
295
|
+
});
|
|
296
|
+
expect(expected[0].map((p) => p.jsonName)).to.deep.equal(["id", "className", "codeValue", "geometryStream"]);
|
|
297
|
+
expect(expected[1].map((p) => p.name)).to.deep.equal(["ECInstanceId", "ECClassId", "CodeValue", "GeometryStream"]);
|
|
298
|
+
iModel.clearCaches();
|
|
299
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
300
|
+
optionSets.forEach((options, i) => {
|
|
301
|
+
const { meta, row } = readMetadata({ ...options });
|
|
302
|
+
expect(meta).to.deep.equal(expected[i]);
|
|
303
|
+
const geometryStream = row[options.rowFormat === QueryRowFormat.UseECSqlPropertyNames ? "GeometryStream" : "geometryStream"];
|
|
304
|
+
if (options.abbreviateBlobs)
|
|
305
|
+
expect(geometryStream).to.be.a("string").and.match(/^\{"bytes":\d+\}$/);
|
|
306
|
+
else
|
|
307
|
+
expect(geometryStream).to.be.instanceOf(Uint8Array);
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
it("fetches native metadata once per statement and row options", () => {
|
|
312
|
+
// Native prototype methods cannot be spied on directly, so count calls through a subclass.
|
|
313
|
+
const getMetadataSpy = sinon.spy();
|
|
314
|
+
const nativeStatement = IModelNative.platform.ECSqlStatement;
|
|
315
|
+
sinon.replace(IModelNative.platform, "ECSqlStatement", class extends nativeStatement {
|
|
316
|
+
getMetadata(...args) {
|
|
317
|
+
getMetadataSpy();
|
|
318
|
+
return super.getMetadata(...args);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
iModel.clearCaches();
|
|
322
|
+
const options = { rowFormat: QueryRowFormat.UseECSqlPropertyNames };
|
|
323
|
+
for (let i = 0; i < 10; i++)
|
|
324
|
+
readMetadata({ ...options });
|
|
325
|
+
expect(getMetadataSpy.callCount).to.equal(1);
|
|
326
|
+
readMetadata({ ...options, abbreviateBlobs: true });
|
|
327
|
+
readMetadata({ ...options, abbreviateBlobs: true });
|
|
328
|
+
expect(getMetadataSpy.callCount).to.equal(2, "different row options need their own metadata");
|
|
329
|
+
iModel.clearCaches();
|
|
330
|
+
readMetadata({ ...options });
|
|
331
|
+
expect(getMetadataSpy.callCount).to.equal(3, "a re-prepared statement must not reuse cached metadata");
|
|
332
|
+
});
|
|
333
|
+
it("does not share metadata objects between calls", () => {
|
|
334
|
+
iModel.clearCaches();
|
|
335
|
+
const options = { rowFormat: QueryRowFormat.UseECSqlPropertyNames };
|
|
336
|
+
const expected = structuredClone(readMetadata({ ...options }).meta);
|
|
337
|
+
iModel.withQueryReader(sql, (reader) => {
|
|
338
|
+
assert.isTrue(reader.step());
|
|
339
|
+
const readerMeta = reader.getMetaData();
|
|
340
|
+
readerMeta[0].name = "Changed";
|
|
341
|
+
readerMeta[0].index = 3;
|
|
342
|
+
readerMeta.reverse();
|
|
343
|
+
const proxyMeta = reader.current.getMetaData();
|
|
344
|
+
proxyMeta.push({ ...proxyMeta[0], name: "Extra" });
|
|
345
|
+
}, new QueryBinder().bindId(1, elementId), options);
|
|
346
|
+
const { meta, row } = readMetadata({ ...options });
|
|
347
|
+
expect(meta).to.deep.equal(expected);
|
|
348
|
+
expect(row.ECInstanceId).to.equal(elementId);
|
|
349
|
+
});
|
|
350
|
+
it("returns metadata for queries that produce no rows", () => {
|
|
351
|
+
iModel.clearCaches();
|
|
352
|
+
const options = { rowFormat: QueryRowFormat.UseECSqlPropertyNames };
|
|
353
|
+
const expected = readMetadata({ ...options }).meta;
|
|
354
|
+
for (let i = 0; i < 3; i++) {
|
|
355
|
+
iModel.withQueryReader(sql, (reader) => {
|
|
356
|
+
expect(reader.step()).to.be.false;
|
|
357
|
+
expect(reader.getMetaData()).to.deep.equal(expected);
|
|
358
|
+
}, new QueryBinder().bindId(1, "0xffffff"), options);
|
|
359
|
+
iModel.withQueryReader(sql, (reader) => {
|
|
360
|
+
expect(reader.getMetaData()).to.deep.equal(expected);
|
|
361
|
+
expect(reader.toArray()).to.deep.equal([]);
|
|
362
|
+
}, new QueryBinder().bindId(1, "0xffffff"), options);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
it("reflects a schema import between calls", () => {
|
|
366
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
367
|
+
try {
|
|
368
|
+
const schemaXml = (version, extraProperty) => `<ECSchema schemaName="Test" alias="ts" version="${version}" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
|
|
369
|
+
<ECEntityClass typeName="Foo" modifier="Sealed">
|
|
370
|
+
<ECProperty propertyName="n" typeName="int"/>
|
|
371
|
+
${extraProperty}
|
|
372
|
+
</ECEntityClass>
|
|
373
|
+
</ECSchema>`;
|
|
374
|
+
const ecdb = __addDisposableResource(env_2, ECDbTestHelper.createECDb(KnownTestLocations.outputDir, "syncReaderMetadataCache.ecdb", schemaXml("01.00.00", "")), false);
|
|
375
|
+
ecdb.withCachedWriteStatement("INSERT INTO ts.Foo(n) VALUES(1)", (stmt) => stmt.stepForInsert());
|
|
376
|
+
ecdb.saveChanges();
|
|
377
|
+
const fooSql = "SELECT * FROM ts.Foo";
|
|
378
|
+
const options = { rowFormat: QueryRowFormat.UseECSqlPropertyNames };
|
|
379
|
+
const readNames = () => ecdb.withQueryReader(fooSql, (reader) => {
|
|
380
|
+
assert.isTrue(reader.step());
|
|
381
|
+
return reader.getMetaData().map((p) => p.name);
|
|
382
|
+
}, undefined, options);
|
|
383
|
+
expect(readNames()).to.deep.equal(["ECInstanceId", "ECClassId", "n"]);
|
|
384
|
+
expect(readNames()).to.deep.equal(["ECInstanceId", "ECClassId", "n"]);
|
|
385
|
+
const schemaPath = path.join(KnownTestLocations.outputDir, "syncReaderMetadataCache.ecschema.xml");
|
|
386
|
+
IModelJsFs.writeFileSync(schemaPath, schemaXml("01.00.01", `<ECProperty propertyName="m" typeName="string"/>`));
|
|
387
|
+
ecdb.importSchema(schemaPath);
|
|
388
|
+
ecdb.saveChanges();
|
|
389
|
+
expect(readNames()).to.deep.equal(["ECInstanceId", "ECClassId", "n", "m"]);
|
|
390
|
+
}
|
|
391
|
+
catch (e_2) {
|
|
392
|
+
env_2.error = e_2;
|
|
393
|
+
env_2.hasError = true;
|
|
394
|
+
}
|
|
395
|
+
finally {
|
|
396
|
+
__disposeResources(env_2);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
});
|
|
260
400
|
});
|
|
261
401
|
//# sourceMappingURL=ECSqlSyncReader.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ECSqlSyncReader.test.js","sourceRoot":"","sources":["../../../../src/test/ecdb/ECSqlSyncReader.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { assert, expect } from \"chai\";\r\nimport * as sinon from \"sinon\";\r\nimport { QueryBinder, QueryOptionsBuilder, QueryRowFormat } from \"@itwin/core-common\";\r\nimport { SnapshotDb } from \"../../IModelDb\";\r\nimport { IModelTestUtils } from \"../IModelTestUtils\";\r\nimport { Id64 } from \"@itwin/core-bentley\";\r\nimport { ECSqlStatement } from \"../../ECSqlStatement\";\r\nimport { ECDbTestHelper } from \"./ECDbTestHelper\";\r\nimport { KnownTestLocations } from \"../KnownTestLocations\";\r\n\r\ndescribe(\"WithQueryReaderTests\", () => {\r\n let iModel: SnapshotDb;\r\n\r\n before(async () => {\r\n iModel = SnapshotDb.openFile(IModelTestUtils.resolveAssetFile(\"test.bim\"));\r\n });\r\n\r\n after(async () => {\r\n iModel.close();\r\n });\r\n it(\"check behvaiour if we call clearCaches in between\", () => {\r\n let actualRowCount = 0;\r\n const expectedInstanceIds = [\"0x1\", \"0xe\", \"0x10\", \"0x11\", \"0x12\",\r\n \"0x13\", \"0x14\", \"0x15\", \"0x16\", \"0x17\"];\r\n\r\n expect(() => iModel.withQueryReader(\"SELECT * FROM bis.Element\", (reader) => {\r\n let loopCount = 0;\r\n // First loop - read first 10 rows\r\n while (loopCount < 10) {\r\n reader.step()\r\n actualRowCount++;\r\n loopCount++;\r\n assert.isDefined(reader.current[0]);\r\n assert.equal(reader.current[0], expectedInstanceIds[actualRowCount - 1]);\r\n }\r\n assert.equal(loopCount, 10);\r\n iModel.clearCaches();\r\n\r\n reader.step(); // step should fail after clearCaches\r\n })).to.throw(\"Step failed\");\r\n });\r\n it(\"should throw error if we try to step on a closed iModelDb object\", () => {\r\n const imodelPath = iModel.pathName;\r\n expect(() => iModel.withQueryReader(\"SELECT * FROM bis.Element\", (reader) => {\r\n let loopCount = 0;\r\n while (loopCount < 10) {\r\n reader.step();\r\n assert.isDefined(reader.current[0]);\r\n loopCount++;\r\n }\r\n assert.equal(loopCount, 10);\r\n iModel.close();\r\n\r\n iModel = SnapshotDb.openFile(imodelPath);\r\n assert.isDefined(reader.current[0]);\r\n reader.step(); // step should fail after iModelDb is closed\r\n })).to.throw(\"Statement is not prepared\");\r\n });\r\n it(\"returning reader from withQueryReader callback should throw error if we try to step on it\", () => {\r\n const readerObj = iModel.withQueryReader(\"SELECT * FROM bis.Element\", (reader) => {\r\n reader.step();\r\n return reader;\r\n });\r\n expect(readerObj.current[0]).to.equal(\"0x1\"); // will not throw error as we are just accessing current row\r\n expect(() => readerObj.step()).to.throw(\"Statement is not prepared\");\r\n });\r\n it(\"checking rowFormat unspecified case - values accessed by index\", () => {\r\n // Default rowFormat is UseECSqlPropertyIndexes: columns are accessed by their SELECT-order index.\r\n iModel.withQueryReader(\"SELECT ECInstanceId, ECClassId FROM bis.Element\", (reader) => {\r\n assert.isTrue(reader.step());\r\n const id: string = reader.current[0];\r\n const classId: string = reader.current[1];\r\n assert.isTrue(Id64.isValid(classId));\r\n assert.isTrue(Id64.isValid(id));\r\n assert.equal(id, \"0x19\");\r\n });\r\n });\r\n\r\n it(\"checking rowFormat UseECSqlPropertyIndexes - values accessed by index\", () => {\r\n const config = new QueryOptionsBuilder().setRowFormat(QueryRowFormat.UseECSqlPropertyIndexes).getOptions();\r\n iModel.withQueryReader(\"SELECT ECInstanceId, ECClassId FROM bis.Element\", (reader) => {\r\n assert.isTrue(reader.step());\r\n // Index 0 → ECInstanceId, index 1 → ECClassId\r\n const id: string = reader.current[0];\r\n const classId: string = reader.current[1];\r\n assert.equal(id, \"0x19\");\r\n assert.isTrue(Id64.isValid(classId));\r\n // Swapping column order changes index, not value\r\n }, undefined, config);\r\n });\r\n\r\n it(\"checking rowFormat UseECSqlPropertyNames - values accessed by ECSQL property name\", () => {\r\n const config = new QueryOptionsBuilder().setRowFormat(QueryRowFormat.UseECSqlPropertyNames).getOptions();\r\n iModel.withQueryReader(\"SELECT ECInstanceId, ECClassId FROM bis.Element\", (reader) => {\r\n assert.isTrue(reader.step());\r\n const id: string = reader.current.ECInstanceId;\r\n const classId: string = reader.current.ECClassId;\r\n assert.equal(id, \"0x19\");\r\n assert.isTrue(Id64.isValid(classId));\r\n }, undefined, config);\r\n });\r\n\r\n it(\"checking rowFormat UseJsPropertyNames - values accessed by JavaScript property name\", () => {\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n const config = new QueryOptionsBuilder().setRowFormat(QueryRowFormat.UseJsPropertyNames).getOptions();\r\n iModel.withQueryReader(\"SELECT ECInstanceId, ECClassId FROM bis.Element\", (reader) => {\r\n assert.isTrue(reader.step());\r\n // ECInstanceId → id, ECClassId → className (resolved to a fully-qualified class name string)\r\n const id: string = reader.current.id;\r\n const className: string = reader.current.className;\r\n assert.equal(id, \"0x19\");\r\n // className should be in the form \"SchemaName.ClassName\"\r\n assert.equal(className, \"BisCore.DrawingCategory\");\r\n }, undefined, config);\r\n });\r\n\r\n it(\"checking rowFormat UseECSqlPropertyNames with convertClassIdsToClassNames - ECClassId returned as class name string\", () => {\r\n const config = new QueryOptionsBuilder()\r\n .setRowFormat(QueryRowFormat.UseECSqlPropertyNames)\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n .setConvertClassIdsToNames(true)\r\n .getOptions();\r\n iModel.withQueryReader(\"SELECT ECInstanceId, ECClassId FROM bis.Element\", (reader) => {\r\n assert.isTrue(reader.step());\r\n const id: string = reader.current.ECInstanceId;\r\n const classId: string = reader.current.ECClassId;\r\n assert.equal(id, \"0x19\");\r\n // With convertClassIdsToClassNames, ECClassId is resolved to \"SchemaName.ClassName\" instead of an Id\r\n assert.equal(classId, \"BisCore.DrawingCategory\");\r\n }, undefined, config);\r\n });\r\n\r\n describe(\"statement caching (regression)\", () => {\r\n // `withQueryReader` used to build a fresh `ECSqlStatement` and re-compile the ECSQL on every\r\n // call (via `ECSqlRowExecutor`), with no caching. Callers such as `IModelTransformer` invoke\r\n // per-element helpers (e.g. `hasSubModel`) once per element, so on large models this turned one\r\n // cached ECSQL prepare into N full native compiles - a significant performance regression. The\r\n // reader now reuses a prepared statement from the owning db's `_statementCache`.\r\n const sql = \"SELECT ECInstanceId FROM bis.Element WHERE ECInstanceId=?\";\r\n\r\n afterEach(() => sinon.restore());\r\n\r\n it(\"reuses a single prepared statement across repeated calls instead of re-preparing\", () => {\r\n iModel.clearCaches();\r\n const prepareSpy = sinon.spy(ECSqlStatement.prototype, \"prepare\"); // eslint-disable-line @typescript-eslint/no-deprecated\r\n for (let i = 0; i < 20; i++) {\r\n iModel.withQueryReader(sql, (reader) => {\r\n reader.step();\r\n }, new QueryBinder().bindId(1, \"0x1\"));\r\n }\r\n expect(prepareSpy.callCount).to.equal(1, \"queries with identical ECSQL text should re-use one cached prepared statement\");\r\n });\r\n\r\n it(\"keeps nested withQueryReader calls on the same SQL independent\", () => {\r\n iModel.clearCaches();\r\n // A nested reader on the same SQL must get its own statement (findAndRemove hands the outer\r\n // reader's statement out of the cache), so results do not corrupt each other.\r\n const outerId = iModel.withQueryReader(sql, (outer) => {\r\n assert.isTrue(outer.step());\r\n const innerId = iModel.withQueryReader(sql, (inner) => {\r\n assert.isTrue(inner.step());\r\n return inner.current[0] as string;\r\n }, new QueryBinder().bindId(1, \"0xe\"));\r\n expect(innerId).to.equal(\"0xe\");\r\n // Outer reader's cursor must be unaffected by the nested reader.\r\n return outer.current[0] as string;\r\n }, new QueryBinder().bindId(1, \"0x1\"));\r\n expect(outerId).to.equal(\"0x1\");\r\n });\r\n\r\n it(\"recovers from a prepare failure without poisoning the cache\", () => {\r\n iModel.clearCaches();\r\n const invalidSql = \"SELECT * FROM bis.ThisClassDoesNotExist\";\r\n const prepareSpy = sinon.spy(ECSqlStatement.prototype, \"prepare\"); // eslint-disable-line @typescript-eslint/no-deprecated\r\n expect(() => iModel.withQueryReader(invalidSql, (reader) => reader.step())).to.throw();\r\n expect(() => iModel.withQueryReader(invalidSql, (reader) => reader.step())).to.throw();\r\n expect(prepareSpy.callCount).to.equal(2, \"a statement that failed to prepare must not be cached\");\r\n\r\n iModel.withQueryReader(sql, (reader) => {\r\n assert.isTrue(reader.step());\r\n expect(reader.current[0]).to.equal(\"0x1\");\r\n }, new QueryBinder().bindId(1, \"0x1\"));\r\n });\r\n\r\n it(\"releases the executor and cached statement when binding fails during reader construction\", () => {\r\n iModel.clearCaches();\r\n const prepareSpy = sinon.spy(ECSqlStatement.prototype, \"prepare\"); // eslint-disable-line @typescript-eslint/no-deprecated\r\n iModel.withQueryReader(sql, (reader) => reader.step(), new QueryBinder().bindId(1, \"0x1\"));\r\n const listenerCount = iModel.onBeforeClose.numberOfListeners;\r\n\r\n const bindStub = sinon.stub(ECSqlStatement.prototype, \"bindParams\").throws(new Error(\"Expected bind failure\")); // eslint-disable-line @typescript-eslint/no-deprecated\r\n expect(() => iModel.withQueryReader(sql, () => undefined, new QueryBinder().bindId(1, \"0x1\"))).to.throw(\"Expected bind failure\");\r\n bindStub.restore();\r\n expect(iModel.onBeforeClose.numberOfListeners).to.equal(listenerCount);\r\n\r\n iModel.withQueryReader(sql, (reader) => reader.step(), new QueryBinder().bindId(1, \"0x1\"));\r\n expect(prepareSpy.callCount).to.equal(1, \"the statement checked out before the bind failure should be returned to the cache\");\r\n });\r\n\r\n it(\"also caches for ECDb.withQueryReader (shared ECSqlRowExecutor path)\", () => {\r\n using ecdb = ECDbTestHelper.createECDb(KnownTestLocations.outputDir, \"syncReaderStmtCache.ecdb\",\r\n `<ECSchema schemaName=\"Test\" alias=\"ts\" version=\"01.00.00\" xmlns=\"http://www.bentley.com/schemas/Bentley.ECXML.3.2\">\r\n <ECEntityClass typeName=\"Foo\" modifier=\"Sealed\">\r\n <ECProperty propertyName=\"n\" typeName=\"int\"/>\r\n </ECEntityClass>\r\n </ECSchema>`);\r\n ecdb.saveChanges();\r\n const ecdbSql = \"SELECT ECInstanceId FROM meta.ECClassDef WHERE ECInstanceId=?\";\r\n const prepareSpy = sinon.spy(ECSqlStatement.prototype, \"prepare\"); // eslint-disable-line @typescript-eslint/no-deprecated\r\n for (let i = 0; i < 10; i++)\r\n ecdb.withQueryReader(ecdbSql, (reader) => reader.step(), new QueryBinder().bindId(1, \"0x1\"));\r\n expect(prepareSpy.callCount).to.equal(1, \"ECDb.withQueryReader should also reuse a cached prepared statement\");\r\n });\r\n });\r\n});\r\n"]}
|
|
1
|
+
{"version":3,"file":"ECSqlSyncReader.test.js","sourceRoot":"","sources":["../../../../src/test/ecdb/ECSqlSyncReader.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { assert, expect } from \"chai\";\r\nimport * as sinon from \"sinon\";\r\nimport * as path from \"node:path\";\r\nimport { QueryBinder, QueryOptionsBuilder, QueryPropertyMetaData, QueryRowFormat } from \"@itwin/core-common\";\r\nimport { SnapshotDb } from \"../../IModelDb\";\r\nimport { IModelTestUtils } from \"../IModelTestUtils\";\r\nimport { Id64 } from \"@itwin/core-bentley\";\r\nimport { ECSqlStatement } from \"../../ECSqlStatement\";\r\nimport { ECDbTestHelper } from \"./ECDbTestHelper\";\r\nimport { KnownTestLocations } from \"../KnownTestLocations\";\r\nimport { IModelNative } from \"../../internal/NativePlatform\";\r\nimport { IModelJsFs } from \"../../IModelJsFs\";\r\nimport { SynchronousQueryOptions } from \"../../ECSqlSyncReader\";\r\n\r\ndescribe(\"WithQueryReaderTests\", () => {\r\n let iModel: SnapshotDb;\r\n\r\n before(async () => {\r\n iModel = SnapshotDb.openFile(IModelTestUtils.resolveAssetFile(\"test.bim\"));\r\n });\r\n\r\n after(async () => {\r\n iModel.close();\r\n });\r\n it(\"check behvaiour if we call clearCaches in between\", () => {\r\n let actualRowCount = 0;\r\n const expectedInstanceIds = [\"0x1\", \"0xe\", \"0x10\", \"0x11\", \"0x12\",\r\n \"0x13\", \"0x14\", \"0x15\", \"0x16\", \"0x17\"];\r\n\r\n expect(() => iModel.withQueryReader(\"SELECT * FROM bis.Element\", (reader) => {\r\n let loopCount = 0;\r\n // First loop - read first 10 rows\r\n while (loopCount < 10) {\r\n reader.step()\r\n actualRowCount++;\r\n loopCount++;\r\n assert.isDefined(reader.current[0]);\r\n assert.equal(reader.current[0], expectedInstanceIds[actualRowCount - 1]);\r\n }\r\n assert.equal(loopCount, 10);\r\n iModel.clearCaches();\r\n\r\n reader.step(); // step should fail after clearCaches\r\n })).to.throw(\"Step failed\");\r\n });\r\n it(\"should throw error if we try to step on a closed iModelDb object\", () => {\r\n const imodelPath = iModel.pathName;\r\n expect(() => iModel.withQueryReader(\"SELECT * FROM bis.Element\", (reader) => {\r\n let loopCount = 0;\r\n while (loopCount < 10) {\r\n reader.step();\r\n assert.isDefined(reader.current[0]);\r\n loopCount++;\r\n }\r\n assert.equal(loopCount, 10);\r\n iModel.close();\r\n\r\n iModel = SnapshotDb.openFile(imodelPath);\r\n assert.isDefined(reader.current[0]);\r\n reader.step(); // step should fail after iModelDb is closed\r\n })).to.throw(\"Statement is not prepared\");\r\n });\r\n it(\"returning reader from withQueryReader callback should throw error if we try to step on it\", () => {\r\n const readerObj = iModel.withQueryReader(\"SELECT * FROM bis.Element\", (reader) => {\r\n reader.step();\r\n return reader;\r\n });\r\n expect(readerObj.current[0]).to.equal(\"0x1\"); // will not throw error as we are just accessing current row\r\n expect(() => readerObj.step()).to.throw(\"Statement is not prepared\");\r\n });\r\n it(\"checking rowFormat unspecified case - values accessed by index\", () => {\r\n // Default rowFormat is UseECSqlPropertyIndexes: columns are accessed by their SELECT-order index.\r\n iModel.withQueryReader(\"SELECT ECInstanceId, ECClassId FROM bis.Element\", (reader) => {\r\n assert.isTrue(reader.step());\r\n const id: string = reader.current[0];\r\n const classId: string = reader.current[1];\r\n assert.isTrue(Id64.isValid(classId));\r\n assert.isTrue(Id64.isValid(id));\r\n assert.equal(id, \"0x19\");\r\n });\r\n });\r\n\r\n it(\"checking rowFormat UseECSqlPropertyIndexes - values accessed by index\", () => {\r\n const config = new QueryOptionsBuilder().setRowFormat(QueryRowFormat.UseECSqlPropertyIndexes).getOptions();\r\n iModel.withQueryReader(\"SELECT ECInstanceId, ECClassId FROM bis.Element\", (reader) => {\r\n assert.isTrue(reader.step());\r\n // Index 0 → ECInstanceId, index 1 → ECClassId\r\n const id: string = reader.current[0];\r\n const classId: string = reader.current[1];\r\n assert.equal(id, \"0x19\");\r\n assert.isTrue(Id64.isValid(classId));\r\n // Swapping column order changes index, not value\r\n }, undefined, config);\r\n });\r\n\r\n it(\"checking rowFormat UseECSqlPropertyNames - values accessed by ECSQL property name\", () => {\r\n const config = new QueryOptionsBuilder().setRowFormat(QueryRowFormat.UseECSqlPropertyNames).getOptions();\r\n iModel.withQueryReader(\"SELECT ECInstanceId, ECClassId FROM bis.Element\", (reader) => {\r\n assert.isTrue(reader.step());\r\n const id: string = reader.current.ECInstanceId;\r\n const classId: string = reader.current.ECClassId;\r\n assert.equal(id, \"0x19\");\r\n assert.isTrue(Id64.isValid(classId));\r\n }, undefined, config);\r\n });\r\n\r\n it(\"checking rowFormat UseJsPropertyNames - values accessed by JavaScript property name\", () => {\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n const config = new QueryOptionsBuilder().setRowFormat(QueryRowFormat.UseJsPropertyNames).getOptions();\r\n iModel.withQueryReader(\"SELECT ECInstanceId, ECClassId FROM bis.Element\", (reader) => {\r\n assert.isTrue(reader.step());\r\n // ECInstanceId → id, ECClassId → className (resolved to a fully-qualified class name string)\r\n const id: string = reader.current.id;\r\n const className: string = reader.current.className;\r\n assert.equal(id, \"0x19\");\r\n // className should be in the form \"SchemaName.ClassName\"\r\n assert.equal(className, \"BisCore.DrawingCategory\");\r\n }, undefined, config);\r\n });\r\n\r\n it(\"checking rowFormat UseECSqlPropertyNames with convertClassIdsToClassNames - ECClassId returned as class name string\", () => {\r\n const config = new QueryOptionsBuilder()\r\n .setRowFormat(QueryRowFormat.UseECSqlPropertyNames)\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n .setConvertClassIdsToNames(true)\r\n .getOptions();\r\n iModel.withQueryReader(\"SELECT ECInstanceId, ECClassId FROM bis.Element\", (reader) => {\r\n assert.isTrue(reader.step());\r\n const id: string = reader.current.ECInstanceId;\r\n const classId: string = reader.current.ECClassId;\r\n assert.equal(id, \"0x19\");\r\n // With convertClassIdsToClassNames, ECClassId is resolved to \"SchemaName.ClassName\" instead of an Id\r\n assert.equal(classId, \"BisCore.DrawingCategory\");\r\n }, undefined, config);\r\n });\r\n\r\n describe(\"statement caching (regression)\", () => {\r\n // `withQueryReader` used to build a fresh `ECSqlStatement` and re-compile the ECSQL on every\r\n // call (via `ECSqlRowExecutor`), with no caching. Callers such as `IModelTransformer` invoke\r\n // per-element helpers (e.g. `hasSubModel`) once per element, so on large models this turned one\r\n // cached ECSQL prepare into N full native compiles - a significant performance regression. The\r\n // reader now reuses a prepared statement from the owning db's `_statementCache`.\r\n const sql = \"SELECT ECInstanceId FROM bis.Element WHERE ECInstanceId=?\";\r\n\r\n afterEach(() => sinon.restore());\r\n\r\n it(\"reuses a single prepared statement across repeated calls instead of re-preparing\", () => {\r\n iModel.clearCaches();\r\n const prepareSpy = sinon.spy(ECSqlStatement.prototype, \"prepare\"); // eslint-disable-line @typescript-eslint/no-deprecated\r\n for (let i = 0; i < 20; i++) {\r\n iModel.withQueryReader(sql, (reader) => {\r\n reader.step();\r\n }, new QueryBinder().bindId(1, \"0x1\"));\r\n }\r\n expect(prepareSpy.callCount).to.equal(1, \"queries with identical ECSQL text should re-use one cached prepared statement\");\r\n });\r\n\r\n it(\"keeps nested withQueryReader calls on the same SQL independent\", () => {\r\n iModel.clearCaches();\r\n // A nested reader on the same SQL must get its own statement (findAndRemove hands the outer\r\n // reader's statement out of the cache), so results do not corrupt each other.\r\n const outerId = iModel.withQueryReader(sql, (outer) => {\r\n assert.isTrue(outer.step());\r\n const innerId = iModel.withQueryReader(sql, (inner) => {\r\n assert.isTrue(inner.step());\r\n return inner.current[0] as string;\r\n }, new QueryBinder().bindId(1, \"0xe\"));\r\n expect(innerId).to.equal(\"0xe\");\r\n // Outer reader's cursor must be unaffected by the nested reader.\r\n return outer.current[0] as string;\r\n }, new QueryBinder().bindId(1, \"0x1\"));\r\n expect(outerId).to.equal(\"0x1\");\r\n });\r\n\r\n it(\"recovers from a prepare failure without poisoning the cache\", () => {\r\n iModel.clearCaches();\r\n const invalidSql = \"SELECT * FROM bis.ThisClassDoesNotExist\";\r\n const prepareSpy = sinon.spy(ECSqlStatement.prototype, \"prepare\"); // eslint-disable-line @typescript-eslint/no-deprecated\r\n expect(() => iModel.withQueryReader(invalidSql, (reader) => reader.step())).to.throw();\r\n expect(() => iModel.withQueryReader(invalidSql, (reader) => reader.step())).to.throw();\r\n expect(prepareSpy.callCount).to.equal(2, \"a statement that failed to prepare must not be cached\");\r\n\r\n iModel.withQueryReader(sql, (reader) => {\r\n assert.isTrue(reader.step());\r\n expect(reader.current[0]).to.equal(\"0x1\");\r\n }, new QueryBinder().bindId(1, \"0x1\"));\r\n });\r\n\r\n it(\"releases the executor and cached statement when binding fails during reader construction\", () => {\r\n iModel.clearCaches();\r\n const prepareSpy = sinon.spy(ECSqlStatement.prototype, \"prepare\"); // eslint-disable-line @typescript-eslint/no-deprecated\r\n iModel.withQueryReader(sql, (reader) => reader.step(), new QueryBinder().bindId(1, \"0x1\"));\r\n const listenerCount = iModel.onBeforeClose.numberOfListeners;\r\n\r\n const bindStub = sinon.stub(ECSqlStatement.prototype, \"bindParams\").throws(new Error(\"Expected bind failure\")); // eslint-disable-line @typescript-eslint/no-deprecated\r\n expect(() => iModel.withQueryReader(sql, () => undefined, new QueryBinder().bindId(1, \"0x1\"))).to.throw(\"Expected bind failure\");\r\n bindStub.restore();\r\n expect(iModel.onBeforeClose.numberOfListeners).to.equal(listenerCount);\r\n\r\n iModel.withQueryReader(sql, (reader) => reader.step(), new QueryBinder().bindId(1, \"0x1\"));\r\n expect(prepareSpy.callCount).to.equal(1, \"the statement checked out before the bind failure should be returned to the cache\");\r\n });\r\n\r\n it(\"also caches for ECDb.withQueryReader (shared ECSqlRowExecutor path)\", () => {\r\n using ecdb = ECDbTestHelper.createECDb(KnownTestLocations.outputDir, \"syncReaderStmtCache.ecdb\",\r\n `<ECSchema schemaName=\"Test\" alias=\"ts\" version=\"01.00.00\" xmlns=\"http://www.bentley.com/schemas/Bentley.ECXML.3.2\">\r\n <ECEntityClass typeName=\"Foo\" modifier=\"Sealed\">\r\n <ECProperty propertyName=\"n\" typeName=\"int\"/>\r\n </ECEntityClass>\r\n </ECSchema>`);\r\n ecdb.saveChanges();\r\n const ecdbSql = \"SELECT ECInstanceId FROM meta.ECClassDef WHERE ECInstanceId=?\";\r\n const prepareSpy = sinon.spy(ECSqlStatement.prototype, \"prepare\"); // eslint-disable-line @typescript-eslint/no-deprecated\r\n for (let i = 0; i < 10; i++)\r\n ecdb.withQueryReader(ecdbSql, (reader) => reader.step(), new QueryBinder().bindId(1, \"0x1\"));\r\n expect(prepareSpy.callCount).to.equal(1, \"ECDb.withQueryReader should also reuse a cached prepared statement\");\r\n });\r\n });\r\n\r\n describe(\"metadata caching\", () => {\r\n // Column metadata depends only on the prepared statement and the row-adaptor options, so it is cached\r\n // on the cached ECSqlStatement instead of being fetched from native code on every withQueryReader call.\r\n const sql = \"SELECT ECInstanceId, ECClassId, CodeValue, GeometryStream FROM bis.GeometricElement3d WHERE ECInstanceId=?\";\r\n let elementId: string;\r\n\r\n before(() => {\r\n elementId = iModel.withQueryReader(\"SELECT ECInstanceId FROM bis.GeometricElement3d WHERE GeometryStream IS NOT NULL LIMIT 1\", (reader) => {\r\n assert.isTrue(reader.step());\r\n return reader.current[0] as string;\r\n });\r\n });\r\n\r\n afterEach(() => sinon.restore());\r\n\r\n function readMetadata(options: SynchronousQueryOptions): { meta: QueryPropertyMetaData[], row: any } {\r\n return iModel.withQueryReader(sql, (reader) => {\r\n assert.isTrue(reader.step());\r\n return { meta: reader.getMetaData(), row: reader.current.toRow() };\r\n }, new QueryBinder().bindId(1, elementId), options);\r\n }\r\n\r\n it(\"returns metadata matching the row options when the same statement is reused with different options\", () => {\r\n const optionSets: SynchronousQueryOptions[] = [\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n { rowFormat: QueryRowFormat.UseJsPropertyNames },\r\n { rowFormat: QueryRowFormat.UseECSqlPropertyNames },\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n { rowFormat: QueryRowFormat.UseJsPropertyNames, abbreviateBlobs: true },\r\n { rowFormat: QueryRowFormat.UseECSqlPropertyNames, abbreviateBlobs: true },\r\n { rowFormat: QueryRowFormat.UseECSqlPropertyNames, convertClassIdsToClassNames: true },\r\n ];\r\n\r\n // Expected metadata comes from a freshly prepared statement for each option set.\r\n const expected = optionSets.map((options) => {\r\n iModel.clearCaches();\r\n return readMetadata({ ...options }).meta;\r\n });\r\n expect(expected[0].map((p) => p.jsonName)).to.deep.equal([\"id\", \"className\", \"codeValue\", \"geometryStream\"]);\r\n expect(expected[1].map((p) => p.name)).to.deep.equal([\"ECInstanceId\", \"ECClassId\", \"CodeValue\", \"GeometryStream\"]);\r\n\r\n iModel.clearCaches();\r\n for (let pass = 0; pass < 2; pass++) {\r\n optionSets.forEach((options, i) => {\r\n const { meta, row } = readMetadata({ ...options });\r\n expect(meta).to.deep.equal(expected[i]);\r\n const geometryStream = row[options.rowFormat === QueryRowFormat.UseECSqlPropertyNames ? \"GeometryStream\" : \"geometryStream\"];\r\n if (options.abbreviateBlobs)\r\n expect(geometryStream).to.be.a(\"string\").and.match(/^\\{\"bytes\":\\d+\\}$/);\r\n else\r\n expect(geometryStream).to.be.instanceOf(Uint8Array);\r\n });\r\n }\r\n });\r\n\r\n it(\"fetches native metadata once per statement and row options\", () => {\r\n // Native prototype methods cannot be spied on directly, so count calls through a subclass.\r\n const getMetadataSpy = sinon.spy();\r\n const nativeStatement = IModelNative.platform.ECSqlStatement;\r\n sinon.replace(IModelNative.platform, \"ECSqlStatement\", class extends nativeStatement {\r\n public override getMetadata(...args: Parameters<typeof nativeStatement.prototype.getMetadata>) {\r\n getMetadataSpy();\r\n return super.getMetadata(...args);\r\n }\r\n });\r\n iModel.clearCaches();\r\n const options = { rowFormat: QueryRowFormat.UseECSqlPropertyNames };\r\n for (let i = 0; i < 10; i++)\r\n readMetadata({ ...options });\r\n expect(getMetadataSpy.callCount).to.equal(1);\r\n\r\n readMetadata({ ...options, abbreviateBlobs: true });\r\n readMetadata({ ...options, abbreviateBlobs: true });\r\n expect(getMetadataSpy.callCount).to.equal(2, \"different row options need their own metadata\");\r\n\r\n iModel.clearCaches();\r\n readMetadata({ ...options });\r\n expect(getMetadataSpy.callCount).to.equal(3, \"a re-prepared statement must not reuse cached metadata\");\r\n });\r\n\r\n it(\"does not share metadata objects between calls\", () => {\r\n iModel.clearCaches();\r\n const options = { rowFormat: QueryRowFormat.UseECSqlPropertyNames };\r\n const expected = structuredClone(readMetadata({ ...options }).meta);\r\n\r\n iModel.withQueryReader(sql, (reader) => {\r\n assert.isTrue(reader.step());\r\n const readerMeta = reader.getMetaData();\r\n readerMeta[0].name = \"Changed\";\r\n readerMeta[0].index = 3;\r\n readerMeta.reverse();\r\n const proxyMeta = reader.current.getMetaData();\r\n proxyMeta.push({ ...proxyMeta[0], name: \"Extra\" });\r\n }, new QueryBinder().bindId(1, elementId), options);\r\n\r\n const { meta, row } = readMetadata({ ...options });\r\n expect(meta).to.deep.equal(expected);\r\n expect(row.ECInstanceId).to.equal(elementId);\r\n });\r\n\r\n it(\"returns metadata for queries that produce no rows\", () => {\r\n iModel.clearCaches();\r\n const options = { rowFormat: QueryRowFormat.UseECSqlPropertyNames };\r\n const expected = readMetadata({ ...options }).meta;\r\n for (let i = 0; i < 3; i++) {\r\n iModel.withQueryReader(sql, (reader) => {\r\n expect(reader.step()).to.be.false;\r\n expect(reader.getMetaData()).to.deep.equal(expected);\r\n }, new QueryBinder().bindId(1, \"0xffffff\"), options);\r\n iModel.withQueryReader(sql, (reader) => {\r\n expect(reader.getMetaData()).to.deep.equal(expected);\r\n expect(reader.toArray()).to.deep.equal([]);\r\n }, new QueryBinder().bindId(1, \"0xffffff\"), options);\r\n }\r\n });\r\n\r\n it(\"reflects a schema import between calls\", () => {\r\n const schemaXml = (version: string, extraProperty: string) => `<ECSchema schemaName=\"Test\" alias=\"ts\" version=\"${version}\" xmlns=\"http://www.bentley.com/schemas/Bentley.ECXML.3.2\">\r\n <ECEntityClass typeName=\"Foo\" modifier=\"Sealed\">\r\n <ECProperty propertyName=\"n\" typeName=\"int\"/>\r\n ${extraProperty}\r\n </ECEntityClass>\r\n </ECSchema>`;\r\n using ecdb = ECDbTestHelper.createECDb(KnownTestLocations.outputDir, \"syncReaderMetadataCache.ecdb\", schemaXml(\"01.00.00\", \"\"));\r\n ecdb.withCachedWriteStatement(\"INSERT INTO ts.Foo(n) VALUES(1)\", (stmt) => stmt.stepForInsert());\r\n ecdb.saveChanges();\r\n\r\n const fooSql = \"SELECT * FROM ts.Foo\";\r\n const options = { rowFormat: QueryRowFormat.UseECSqlPropertyNames };\r\n const readNames = () => ecdb.withQueryReader(fooSql, (reader) => {\r\n assert.isTrue(reader.step());\r\n return reader.getMetaData().map((p) => p.name);\r\n }, undefined, options);\r\n expect(readNames()).to.deep.equal([\"ECInstanceId\", \"ECClassId\", \"n\"]);\r\n expect(readNames()).to.deep.equal([\"ECInstanceId\", \"ECClassId\", \"n\"]);\r\n\r\n const schemaPath = path.join(KnownTestLocations.outputDir, \"syncReaderMetadataCache.ecschema.xml\");\r\n IModelJsFs.writeFileSync(schemaPath, schemaXml(\"01.00.01\", `<ECProperty propertyName=\"m\" typeName=\"string\"/>`));\r\n ecdb.importSchema(schemaPath);\r\n ecdb.saveChanges();\r\n\r\n expect(readNames()).to.deep.equal([\"ECInstanceId\", \"ECClassId\", \"n\", \"m\"]);\r\n });\r\n });\r\n});\r\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionApplyIndirect.test.d.ts","sourceRoot":"","sources":["../../../../src/test/standalone/SessionApplyIndirect.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
3
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
6
|
+
if (value !== null && value !== void 0) {
|
|
7
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
8
|
+
var dispose, inner;
|
|
9
|
+
if (async) {
|
|
10
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.asyncDispose];
|
|
12
|
+
}
|
|
13
|
+
if (dispose === void 0) {
|
|
14
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
15
|
+
dispose = value[Symbol.dispose];
|
|
16
|
+
if (async) inner = dispose;
|
|
17
|
+
}
|
|
18
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
19
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
20
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
21
|
+
}
|
|
22
|
+
else if (async) {
|
|
23
|
+
env.stack.push({ async: true });
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
};
|
|
27
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
28
|
+
return function (env) {
|
|
29
|
+
function fail(e) {
|
|
30
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
31
|
+
env.hasError = true;
|
|
32
|
+
}
|
|
33
|
+
var r, s = 0;
|
|
34
|
+
function next() {
|
|
35
|
+
while (r = env.stack.pop()) {
|
|
36
|
+
try {
|
|
37
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
38
|
+
if (r.dispose) {
|
|
39
|
+
var result = r.dispose.call(r.value);
|
|
40
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
41
|
+
}
|
|
42
|
+
else s |= 1;
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
fail(e);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
49
|
+
if (env.hasError) throw env.error;
|
|
50
|
+
}
|
|
51
|
+
return next();
|
|
52
|
+
};
|
|
53
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
54
|
+
var e = new Error(message);
|
|
55
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
56
|
+
});
|
|
57
|
+
//=================================================================================================
|
|
58
|
+
// Tests for the "indirect flag is preserved while applying a changeset" behavior.
|
|
59
|
+
//
|
|
60
|
+
// Background (iTwin/itwinjs-backlog#2474, scenario iTwin/itwinjs-backlog#2346, SQLite check-in
|
|
61
|
+
// https://sqlite.org/src/info/9d36067e76):
|
|
62
|
+
//
|
|
63
|
+
// Every row change recorded by a SQLite session is flagged as either "direct" (made explicitly
|
|
64
|
+
// by the user) or "indirect" (a side effect - e.g. produced by a trigger/foreign-key action, or
|
|
65
|
+
// produced by applying a changeset entry that was itself flagged indirect). iTwin surfaces this
|
|
66
|
+
// flag through `SqliteChangesetReader.isIndirect` (and `ChangeInstance.$meta.isIndirectChange`).
|
|
67
|
+
//
|
|
68
|
+
// Before the SQLite fix, when a changeset was applied to a briefcase that had an active change
|
|
69
|
+
// session (as happens during a `pullChanges` that must rebase local, un-pushed txns), EVERY
|
|
70
|
+
// applied row change was re-recorded as DIRECT, regardless of how it was flagged in the incoming
|
|
71
|
+
// changeset. In OpenSite+ this caused a whole site to be flagged as "directly modified" when only
|
|
72
|
+
// a pond (whose trigger indirectly touched the site row) had actually been edited, which broke
|
|
73
|
+
// the Blame feature.
|
|
74
|
+
//
|
|
75
|
+
// The fix makes `sqlite3changeset_apply()` temporarily set the session "indirect" flag while
|
|
76
|
+
// stepping a row change that was flagged indirect in the incoming changeset. As a result the
|
|
77
|
+
// per-change direct/indirect classification is preserved end-to-end: a direct source change is
|
|
78
|
+
// re-recorded direct, an indirect source change is re-recorded indirect.
|
|
79
|
+
//
|
|
80
|
+
// What these tests assert:
|
|
81
|
+
// When briefcase B2 has a local, un-pushed change and pulls a changeset from B1 that contains an
|
|
82
|
+
// INDIRECT change (a `bis_Model` LastMod/GeometryGuid update produced by a trigger when B1
|
|
83
|
+
// inserted an element), the incoming indirect change is applied under B2's active session during
|
|
84
|
+
// rebase. After the fix, that re-recorded change is still INDIRECT in B2; before the fix it would
|
|
85
|
+
// have been DIRECT.
|
|
86
|
+
//
|
|
87
|
+
// NOTE: This test exercises behavior that lives in the native addon (@bentley/imodeljs-native).
|
|
88
|
+
// It can only pass once an addon built from iTwin/imodel-native#1621 (SQLite tag
|
|
89
|
+
// itwin-sqlite-v3.53.4-r3) is published and consumed by itwinjs-core. Until then it is expected
|
|
90
|
+
// to fail locally, which is why it is parked on a branch rather than opened as a PR.
|
|
91
|
+
//=================================================================================================
|
|
92
|
+
import { assert } from "chai";
|
|
93
|
+
import { IModel, SubCategoryAppearance } from "@itwin/core-common";
|
|
94
|
+
import { EditTxn } from "../../EditTxn";
|
|
95
|
+
import { ChannelControl, SpatialCategory, SqliteChangesetReader, } from "../../core-backend";
|
|
96
|
+
import { HubMock } from "../../internal/HubMock";
|
|
97
|
+
import { HubWrappers, KnownTestLocations } from "..";
|
|
98
|
+
import { IModelTestUtils, TestUserType } from "../IModelTestUtils";
|
|
99
|
+
import { TestUtils } from "../TestUtils";
|
|
100
|
+
function startTestTxn(briefcase) {
|
|
101
|
+
const txn = new EditTxn(briefcase, "session apply indirect");
|
|
102
|
+
txn.start();
|
|
103
|
+
return txn;
|
|
104
|
+
}
|
|
105
|
+
describe("Session apply preserves indirect flag (iTwin/itwinjs-backlog#2346)", () => {
|
|
106
|
+
const ctx = {
|
|
107
|
+
accessTokens: { user1: "", user2: "" },
|
|
108
|
+
iModelId: "",
|
|
109
|
+
iTwinId: "",
|
|
110
|
+
modelId: "",
|
|
111
|
+
spatialCategoryId: "",
|
|
112
|
+
iModelName: "SessionApplyIndirect",
|
|
113
|
+
rootSubject: "SessionApplyIndirect",
|
|
114
|
+
openBriefcase: async (user, noLock) => {
|
|
115
|
+
const b = await HubWrappers.downloadAndOpenBriefcase({ accessToken: ctx.accessTokens[user], iTwinId: ctx.iTwinId, iModelId: ctx.iModelId, noLock });
|
|
116
|
+
b.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
117
|
+
return b;
|
|
118
|
+
},
|
|
119
|
+
openB1: async (noLock) => ctx.openBriefcase("user1", noLock),
|
|
120
|
+
openB2: async (noLock) => ctx.openBriefcase("user2", noLock),
|
|
121
|
+
};
|
|
122
|
+
async function insertPhysicalObject(txn) {
|
|
123
|
+
const briefcase = txn.iModel;
|
|
124
|
+
await briefcase.locks.acquireLocks({ shared: ctx.modelId });
|
|
125
|
+
return txn.insertElement(IModelTestUtils.createPhysicalObject(briefcase, ctx.modelId, ctx.spatialCategoryId).toJSON());
|
|
126
|
+
}
|
|
127
|
+
before(async () => {
|
|
128
|
+
await TestUtils.startBackend();
|
|
129
|
+
HubMock.startup("SessionApplyIndirect", KnownTestLocations.outputDir);
|
|
130
|
+
});
|
|
131
|
+
after(async () => {
|
|
132
|
+
HubMock.shutdown();
|
|
133
|
+
});
|
|
134
|
+
beforeEach(async () => {
|
|
135
|
+
ctx.iTwinId = HubMock.iTwinId;
|
|
136
|
+
ctx.accessTokens.user1 = await HubWrappers.getAccessToken(TestUserType.SuperManager);
|
|
137
|
+
ctx.accessTokens.user2 = await HubWrappers.getAccessToken(TestUserType.Regular);
|
|
138
|
+
ctx.iModelId = await HubMock.createNewIModel({ accessToken: ctx.accessTokens.user1, iTwinId: ctx.iTwinId, iModelName: ctx.iModelName, description: ctx.rootSubject });
|
|
139
|
+
assert.isNotEmpty(ctx.iModelId);
|
|
140
|
+
// Seed the iModel with a physical model + spatial category that both briefcases share.
|
|
141
|
+
const b1 = await ctx.openB1();
|
|
142
|
+
const seedTxn = startTestTxn(b1);
|
|
143
|
+
await b1.locks.acquireLocks({ shared: IModel.dictionaryId });
|
|
144
|
+
[, ctx.modelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(seedTxn, IModelTestUtils.getUniqueModelCode(b1, "indirectModel"), true);
|
|
145
|
+
const dictionary = b1.models.getModel(IModel.dictionaryId);
|
|
146
|
+
const categoryCode = IModelTestUtils.getUniqueSpatialCategoryCode(dictionary, "IndirectCategory");
|
|
147
|
+
ctx.spatialCategoryId = SpatialCategory.insert(seedTxn, dictionary.id, categoryCode.value, new SubCategoryAppearance({ color: 0xff0000 }));
|
|
148
|
+
seedTxn.saveChanges();
|
|
149
|
+
await b1.pushChanges({ description: "seed model + category" });
|
|
150
|
+
b1.close();
|
|
151
|
+
});
|
|
152
|
+
it("re-recorded indirect bis_Model update stays indirect after a rebasing pull", async () => {
|
|
153
|
+
// B1 inserts an element. Inserting an element DIRECTLY writes the bis_Element row, and
|
|
154
|
+
// INDIRECTLY updates the owning bis_Model row (LastMod / GeometryGuid) via a trigger. The
|
|
155
|
+
// pushed changeset therefore contains at least one indirect bis_Model update.
|
|
156
|
+
const b1 = await ctx.openB1();
|
|
157
|
+
const b1Txn = startTestTxn(b1);
|
|
158
|
+
await insertPhysicalObject(b1Txn);
|
|
159
|
+
b1Txn.saveChanges("B1 inserts element (indirectly updates model)");
|
|
160
|
+
await b1.pushChanges({ description: "B1 element insert" });
|
|
161
|
+
// B2 makes its own local change into the SAME model but does NOT push it. This leaves B2 with
|
|
162
|
+
// an active change session and un-pushed local txns, so the subsequent pull must REBASE: B1's
|
|
163
|
+
// changeset is applied into B2 while B2's session is recording. This is exactly the path that
|
|
164
|
+
// re-records applied changes and, before the fix, dropped their indirect flag.
|
|
165
|
+
const b2 = await ctx.openB2();
|
|
166
|
+
const b2Txn = startTestTxn(b2);
|
|
167
|
+
await insertPhysicalObject(b2Txn);
|
|
168
|
+
b2Txn.saveChanges("B2 local element insert (un-pushed)");
|
|
169
|
+
// Pulling B1's changeset forces the rebase/apply of B1's indirect bis_Model update under B2's
|
|
170
|
+
// active session. noFastForward guarantees a rebase actually happens rather than a trivial
|
|
171
|
+
// fast-forward (which would not re-record anything).
|
|
172
|
+
await b2.pullChanges();
|
|
173
|
+
// Walk every local txn recorded in B2 (the result of applying B1's changeset and rebasing B2's
|
|
174
|
+
// own txn on top of it) and inspect each bis_Model update. The #2346 invariant: a bis_Model
|
|
175
|
+
// update must NEVER be recorded as a direct change - model LastMod/GeometryGuid changes are
|
|
176
|
+
// always a trigger side effect, so they must stay indirect through the apply/rebase.
|
|
177
|
+
let sawModelUpdate = false;
|
|
178
|
+
let sawDirectModelUpdate = false;
|
|
179
|
+
for (let txnId = b2.txns.queryFirstTxnId(); b2.txns.isTxnIdValid(txnId); txnId = b2.txns.queryNextTxnId(txnId)) {
|
|
180
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
181
|
+
try {
|
|
182
|
+
const reader = __addDisposableResource(env_1, SqliteChangesetReader.openTxn({ db: b2, txnId }), false);
|
|
183
|
+
while (reader.step()) {
|
|
184
|
+
if (reader.tableName === "bis_Model" && reader.op === "Updated") {
|
|
185
|
+
sawModelUpdate = true;
|
|
186
|
+
if (!reader.isIndirect)
|
|
187
|
+
sawDirectModelUpdate = true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
reader.close();
|
|
191
|
+
}
|
|
192
|
+
catch (e_1) {
|
|
193
|
+
env_1.error = e_1;
|
|
194
|
+
env_1.hasError = true;
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
__disposeResources(env_1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// We must have observed at least one bis_Model update (otherwise the test setup is wrong and
|
|
201
|
+
// the assertion below would be vacuously true).
|
|
202
|
+
assert.isTrue(sawModelUpdate, "expected at least one bis_Model update among B2's local changes");
|
|
203
|
+
// The core assertion: after applying B1's changeset under B2's active session, no bis_Model
|
|
204
|
+
// update was re-recorded as a DIRECT change. Before the SQLite fix this would be false because
|
|
205
|
+
// every applied change was recorded direct.
|
|
206
|
+
assert.isFalse(sawDirectModelUpdate, "a bis_Model update was re-recorded as direct; the indirect flag was lost while applying the changeset (iTwin/itwinjs-backlog#2346)");
|
|
207
|
+
b1.close();
|
|
208
|
+
b2.close();
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
//# sourceMappingURL=SessionApplyIndirect.test.js.map
|