@itwin/core-backend 5.14.0-dev.9 → 5.14.2
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 +67 -1
- package/lib/cjs/BriefcaseManager.d.ts +2 -2
- package/lib/cjs/BriefcaseManager.js.map +1 -1
- package/lib/cjs/ChangesetReader.d.ts +1 -1
- package/lib/cjs/ChangesetReader.js +2 -2
- package/lib/cjs/ChangesetReader.js.map +1 -1
- package/lib/cjs/ChangesetReaderTypes.d.ts +5 -2
- package/lib/cjs/ChangesetReaderTypes.d.ts.map +1 -1
- package/lib/cjs/ChangesetReaderTypes.js +3 -0
- package/lib/cjs/ChangesetReaderTypes.js.map +1 -1
- package/lib/cjs/ECDb.d.ts +63 -6
- package/lib/cjs/ECDb.d.ts.map +1 -1
- package/lib/cjs/ECDb.js +62 -6
- package/lib/cjs/ECDb.js.map +1 -1
- package/lib/cjs/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 +7 -2
- package/lib/cjs/ECSqlStatement.d.ts.map +1 -1
- package/lib/cjs/ECSqlStatement.js +17 -4
- 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/cjs/Element.d.ts.map +1 -1
- package/lib/cjs/Element.js +6 -3
- package/lib/cjs/Element.js.map +1 -1
- package/lib/cjs/IModelDb.d.ts +12 -10
- package/lib/cjs/IModelDb.d.ts.map +1 -1
- package/lib/cjs/IModelDb.js +26 -12
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/cjs/SchemaSync.d.ts +2 -2
- package/lib/cjs/SchemaSync.d.ts.map +1 -1
- package/lib/cjs/SchemaSync.js +2 -2
- package/lib/cjs/SchemaSync.js.map +1 -1
- package/lib/cjs/SqliteChangesetReader.d.ts +16 -8
- package/lib/cjs/SqliteChangesetReader.d.ts.map +1 -1
- package/lib/cjs/SqliteChangesetReader.js +11 -1
- package/lib/cjs/SqliteChangesetReader.js.map +1 -1
- package/lib/cjs/TxnManager.d.ts.map +1 -1
- package/lib/cjs/TxnManager.js +4 -0
- package/lib/cjs/TxnManager.js.map +1 -1
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts +90 -5
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.js +59 -1
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.js.map +1 -1
- package/lib/cjs/internal/annotations/fieldSpecs.d.ts +82 -0
- package/lib/cjs/internal/annotations/fieldSpecs.d.ts.map +1 -0
- package/lib/cjs/internal/annotations/fieldSpecs.js +187 -0
- package/lib/cjs/internal/annotations/fieldSpecs.js.map +1 -0
- package/lib/cjs/internal/annotations/fields.d.ts +39 -2
- package/lib/cjs/internal/annotations/fields.d.ts.map +1 -1
- package/lib/cjs/internal/annotations/fields.js +140 -13
- package/lib/cjs/internal/annotations/fields.js.map +1 -1
- package/lib/esm/BriefcaseManager.d.ts +2 -2
- package/lib/esm/BriefcaseManager.js.map +1 -1
- package/lib/esm/ChangesetReader.d.ts +1 -1
- package/lib/esm/ChangesetReader.js +2 -2
- package/lib/esm/ChangesetReader.js.map +1 -1
- package/lib/esm/ChangesetReaderTypes.d.ts +5 -2
- package/lib/esm/ChangesetReaderTypes.d.ts.map +1 -1
- package/lib/esm/ChangesetReaderTypes.js +3 -0
- package/lib/esm/ChangesetReaderTypes.js.map +1 -1
- package/lib/esm/ECDb.d.ts +63 -6
- package/lib/esm/ECDb.d.ts.map +1 -1
- package/lib/esm/ECDb.js +62 -6
- package/lib/esm/ECDb.js.map +1 -1
- package/lib/esm/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 +7 -2
- package/lib/esm/ECSqlStatement.d.ts.map +1 -1
- package/lib/esm/ECSqlStatement.js +17 -4
- 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/Element.d.ts.map +1 -1
- package/lib/esm/Element.js +6 -3
- package/lib/esm/Element.js.map +1 -1
- package/lib/esm/IModelDb.d.ts +12 -10
- package/lib/esm/IModelDb.d.ts.map +1 -1
- package/lib/esm/IModelDb.js +26 -12
- package/lib/esm/IModelDb.js.map +1 -1
- package/lib/esm/SchemaSync.d.ts +2 -2
- package/lib/esm/SchemaSync.d.ts.map +1 -1
- package/lib/esm/SchemaSync.js +2 -2
- package/lib/esm/SchemaSync.js.map +1 -1
- package/lib/esm/SqliteChangesetReader.d.ts +16 -8
- package/lib/esm/SqliteChangesetReader.d.ts.map +1 -1
- package/lib/esm/SqliteChangesetReader.js +11 -1
- package/lib/esm/SqliteChangesetReader.js.map +1 -1
- package/lib/esm/TxnManager.d.ts.map +1 -1
- package/lib/esm/TxnManager.js +4 -0
- package/lib/esm/TxnManager.js.map +1 -1
- package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts +90 -5
- package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
- package/lib/esm/annotations/ElementDrivesTextAnnotation.js +60 -2
- package/lib/esm/annotations/ElementDrivesTextAnnotation.js.map +1 -1
- package/lib/esm/internal/annotations/fieldSpecs.d.ts +82 -0
- package/lib/esm/internal/annotations/fieldSpecs.d.ts.map +1 -0
- package/lib/esm/internal/annotations/fieldSpecs.js +179 -0
- package/lib/esm/internal/annotations/fieldSpecs.js.map +1 -0
- package/lib/esm/internal/annotations/fields.d.ts +39 -2
- package/lib/esm/internal/annotations/fields.d.ts.map +1 -1
- package/lib/esm/internal/annotations/fields.js +140 -13
- package/lib/esm/internal/annotations/fields.js.map +1 -1
- package/lib/esm/test/AnnotationTestUtils.d.ts +9 -0
- package/lib/esm/test/AnnotationTestUtils.d.ts.map +1 -1
- package/lib/esm/test/AnnotationTestUtils.js +17 -0
- package/lib/esm/test/AnnotationTestUtils.js.map +1 -1
- package/lib/esm/test/annotations/FieldFormat.test.d.ts +2 -0
- package/lib/esm/test/annotations/FieldFormat.test.d.ts.map +1 -0
- package/lib/esm/test/annotations/FieldFormat.test.js +738 -0
- package/lib/esm/test/annotations/FieldFormat.test.js.map +1 -0
- package/lib/esm/test/annotations/FieldFormattingLifecycle.test.d.ts +2 -0
- package/lib/esm/test/annotations/FieldFormattingLifecycle.test.d.ts.map +1 -0
- package/lib/esm/test/annotations/FieldFormattingLifecycle.test.js +323 -0
- package/lib/esm/test/annotations/FieldFormattingLifecycle.test.js.map +1 -0
- package/lib/esm/test/annotations/FieldSpecs.test.d.ts +2 -0
- package/lib/esm/test/annotations/FieldSpecs.test.d.ts.map +1 -0
- package/lib/esm/test/annotations/FieldSpecs.test.js +54 -0
- package/lib/esm/test/annotations/FieldSpecs.test.js.map +1 -0
- package/lib/esm/test/annotations/Fields.test.js +221 -27
- package/lib/esm/test/annotations/Fields.test.js.map +1 -1
- package/lib/esm/test/ecdb/CSVImport.test.d.ts +2 -0
- package/lib/esm/test/ecdb/CSVImport.test.d.ts.map +1 -0
- package/lib/esm/test/ecdb/CSVImport.test.js +186 -0
- package/lib/esm/test/ecdb/CSVImport.test.js.map +1 -0
- package/lib/esm/test/ecdb/ConcurrentQuery.test.js +19 -2
- package/lib/esm/test/ecdb/ConcurrentQuery.test.js.map +1 -1
- package/lib/esm/test/ecdb/ECSqlSyncReader.test.js +140 -0
- package/lib/esm/test/ecdb/ECSqlSyncReader.test.js.map +1 -1
- package/lib/esm/test/ecdb/QueryReaders.test.js +105 -30
- package/lib/esm/test/ecdb/QueryReaders.test.js.map +1 -1
- package/lib/esm/test/ecsql/dataset/ECSqlDatasets.d.ts.map +1 -1
- package/lib/esm/test/ecsql/dataset/ECSqlDatasets.js +7 -0
- package/lib/esm/test/ecsql/dataset/ECSqlDatasets.js.map +1 -1
- package/lib/esm/test/ecsql/src/ECSqlTestRunner.test.js +86 -7
- package/lib/esm/test/ecsql/src/ECSqlTestRunner.test.js.map +1 -1
- package/lib/esm/test/hubaccess/CodeSwap.test.js +78 -0
- package/lib/esm/test/hubaccess/CodeSwap.test.js.map +1 -1
- package/lib/esm/test/imodel/IModelElements.test.js +5 -0
- package/lib/esm/test/imodel/IModelElements.test.js.map +1 -1
- package/lib/esm/test/imodel/IModelGeolocation.test.js +3 -3
- package/lib/esm/test/imodel/IModelGeolocation.test.js.map +1 -1
- package/lib/esm/test/standalone/ChangesetReader.test.js +111 -2
- package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
- package/lib/esm/test/standalone/DgnDomainMergeConflict.test.js +61 -0
- package/lib/esm/test/standalone/DgnDomainMergeConflict.test.js.map +1 -1
- package/lib/esm/test/standalone/SQLiteDb.test.d.ts.map +1 -1
- package/lib/esm/test/standalone/SQLiteDb.test.js +116 -0
- package/lib/esm/test/standalone/SQLiteDb.test.js.map +1 -1
- package/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 -13
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
53
|
+
/*---------------------------------------------------------------------------------------------
|
|
54
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
55
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
56
|
+
*--------------------------------------------------------------------------------------------*/
|
|
57
|
+
import { expect } from "chai";
|
|
58
|
+
import * as fs from "fs";
|
|
59
|
+
import * as path from "path";
|
|
60
|
+
import { DbResult } from "@itwin/core-bentley";
|
|
61
|
+
import { KnownTestLocations } from "../KnownTestLocations";
|
|
62
|
+
import { ECDbTestHelper } from "./ECDbTestHelper";
|
|
63
|
+
const testSchema = `<ECSchema schemaName="Test" alias="test" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
|
|
64
|
+
<ECStructClass typeName="Details">
|
|
65
|
+
<ECProperty propertyName="Nickname" typeName="string"/>
|
|
66
|
+
</ECStructClass>
|
|
67
|
+
<ECEntityClass typeName="Person" modifier="Sealed">
|
|
68
|
+
<ECProperty propertyName="Name" typeName="string"/>
|
|
69
|
+
<ECProperty propertyName="Age" typeName="int"/>
|
|
70
|
+
<ECProperty propertyName="Salary" typeName="double"/>
|
|
71
|
+
<ECStructProperty propertyName="Details" typeName="Details"/>
|
|
72
|
+
</ECEntityClass>
|
|
73
|
+
</ECSchema>`;
|
|
74
|
+
const mapping = [
|
|
75
|
+
{ columnIndex: 0, propertyName: "Name" },
|
|
76
|
+
{ columnIndex: 1, propertyName: "Age" },
|
|
77
|
+
{ columnIndex: 2, propertyName: "Salary" },
|
|
78
|
+
{ columnIndex: 3, propertyName: "Details.Nickname" },
|
|
79
|
+
];
|
|
80
|
+
function readPeople(ecdb) {
|
|
81
|
+
const rows = [];
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
83
|
+
ecdb.withPreparedStatement("SELECT Name, Age, Salary, Details.Nickname FROM test.Person ORDER BY Name", (stmt) => {
|
|
84
|
+
while (stmt.step() === DbResult.BE_SQLITE_ROW) {
|
|
85
|
+
const nickname = stmt.getValue(3);
|
|
86
|
+
rows.push({
|
|
87
|
+
name: stmt.getValue(0).getString(),
|
|
88
|
+
age: stmt.getValue(1).getInteger(),
|
|
89
|
+
salary: stmt.getValue(2).getDouble(),
|
|
90
|
+
nickname: nickname.isNull ? undefined : nickname.getString(),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
return rows;
|
|
95
|
+
}
|
|
96
|
+
describe("ECDb CSV import", () => {
|
|
97
|
+
const outDir = KnownTestLocations.outputDir;
|
|
98
|
+
it("imports in-memory CSV data atomically", () => {
|
|
99
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
100
|
+
try {
|
|
101
|
+
const ecdb = __addDisposableResource(env_1, ECDbTestHelper.createECDb(outDir, "csv-import-data.ecdb", testSchema), false);
|
|
102
|
+
expect(ecdb.importCSVData([
|
|
103
|
+
["Alice", "42", "1234.5", "Résumé"],
|
|
104
|
+
["用户", "31", "2500.25", "昵称"],
|
|
105
|
+
], { className: "Test.Person", mapping })).to.equal(2);
|
|
106
|
+
expect(() => ecdb.importCSVData([
|
|
107
|
+
["Valid", "20", "1000", "valid"],
|
|
108
|
+
["Invalid", "not-an-integer", "1001", "invalid"],
|
|
109
|
+
], { className: "Test.Person", mapping })).to.throw(/CSV data row 2 column 1/);
|
|
110
|
+
expect(readPeople(ecdb)).to.deep.equal([
|
|
111
|
+
{ name: "Alice", age: 42, salary: 1234.5, nickname: "Résumé" },
|
|
112
|
+
{ name: "用户", age: 31, salary: 2500.25, nickname: "昵称" },
|
|
113
|
+
]);
|
|
114
|
+
}
|
|
115
|
+
catch (e_1) {
|
|
116
|
+
env_1.error = e_1;
|
|
117
|
+
env_1.hasError = true;
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
__disposeResources(env_1);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
it("streams a CSV file and rolls back malformed input", () => {
|
|
124
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
125
|
+
try {
|
|
126
|
+
const csvPath = path.join(outDir, "csv-import.csv");
|
|
127
|
+
fs.writeFileSync(csvPath, [
|
|
128
|
+
"Ignored,Age,Name,Salary,Nickname",
|
|
129
|
+
'skip,42,"Doe, Jane",1234.5,"said ""hello""\nnext"',
|
|
130
|
+
String.raw `skip,31,用户,2500.25,\N`,
|
|
131
|
+
].join("\r\n"));
|
|
132
|
+
const ecdb = __addDisposableResource(env_2, ECDbTestHelper.createECDb(outDir, "csv-import-file.ecdb", testSchema), false);
|
|
133
|
+
const fileMapping = [
|
|
134
|
+
{ columnIndex: 1, propertyName: "Age" },
|
|
135
|
+
{ columnIndex: 2, propertyName: "Name" },
|
|
136
|
+
{ columnIndex: 3, propertyName: "Salary" },
|
|
137
|
+
{ columnIndex: 4, propertyName: "Details.Nickname" },
|
|
138
|
+
];
|
|
139
|
+
expect(ecdb.importCSVFile(csvPath, {
|
|
140
|
+
className: "Test.Person",
|
|
141
|
+
mapping: fileMapping,
|
|
142
|
+
hasHeader: true,
|
|
143
|
+
nullValue: String.raw `\N`,
|
|
144
|
+
})).to.equal(2);
|
|
145
|
+
fs.writeFileSync(csvPath, [
|
|
146
|
+
"skip,20,Valid,1000,valid",
|
|
147
|
+
'skip,21,"unterminated,1001,invalid',
|
|
148
|
+
].join("\n"));
|
|
149
|
+
expect(() => ecdb.importCSVFile(csvPath, { className: "Test.Person", mapping: fileMapping }))
|
|
150
|
+
.to.throw(/unterminated quoted field/);
|
|
151
|
+
expect(readPeople(ecdb)).to.deep.equal([
|
|
152
|
+
{ name: "Doe, Jane", age: 42, salary: 1234.5, nickname: 'said "hello"\nnext' },
|
|
153
|
+
{ name: "用户", age: 31, salary: 2500.25, nickname: undefined },
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
catch (e_2) {
|
|
157
|
+
env_2.error = e_2;
|
|
158
|
+
env_2.hasError = true;
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
__disposeResources(env_2);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
it("rejects invalid column mappings before calling native code", () => {
|
|
165
|
+
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
166
|
+
try {
|
|
167
|
+
const ecdb = __addDisposableResource(env_3, ECDbTestHelper.createECDb(outDir, "csv-import-mapping.ecdb", testSchema), false);
|
|
168
|
+
expect(() => ecdb.importCSVData([], { className: "Test.Person", mapping: [] })).to.throw(/must not be empty/);
|
|
169
|
+
expect(() => ecdb.importCSVData([], {
|
|
170
|
+
className: "Test.Person",
|
|
171
|
+
mapping: [
|
|
172
|
+
{ columnIndex: 0, propertyName: "Name" },
|
|
173
|
+
{ columnIndex: 0, propertyName: "Age" },
|
|
174
|
+
],
|
|
175
|
+
})).to.throw(/duplicate columnIndex/);
|
|
176
|
+
}
|
|
177
|
+
catch (e_3) {
|
|
178
|
+
env_3.error = e_3;
|
|
179
|
+
env_3.hasError = true;
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
__disposeResources(env_3);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
//# sourceMappingURL=CSVImport.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CSVImport.test.js","sourceRoot":"","sources":["../../../../src/test/ecdb/CSVImport.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\nimport { expect } from \"chai\";\r\nimport * as fs from \"fs\";\r\nimport * as path from \"path\";\r\nimport { DbResult } from \"@itwin/core-bentley\";\r\nimport { ECDb, ECSqlStatement } from \"../../core-backend\";\r\nimport { KnownTestLocations } from \"../KnownTestLocations\";\r\nimport { ECDbTestHelper } from \"./ECDbTestHelper\";\r\n\r\nconst testSchema = `<ECSchema schemaName=\"Test\" alias=\"test\" version=\"01.00.00\" xmlns=\"http://www.bentley.com/schemas/Bentley.ECXML.3.2\">\r\n <ECStructClass typeName=\"Details\">\r\n <ECProperty propertyName=\"Nickname\" typeName=\"string\"/>\r\n </ECStructClass>\r\n <ECEntityClass typeName=\"Person\" modifier=\"Sealed\">\r\n <ECProperty propertyName=\"Name\" typeName=\"string\"/>\r\n <ECProperty propertyName=\"Age\" typeName=\"int\"/>\r\n <ECProperty propertyName=\"Salary\" typeName=\"double\"/>\r\n <ECStructProperty propertyName=\"Details\" typeName=\"Details\"/>\r\n </ECEntityClass>\r\n</ECSchema>`;\r\n\r\nconst mapping = [\r\n { columnIndex: 0, propertyName: \"Name\" },\r\n { columnIndex: 1, propertyName: \"Age\" },\r\n { columnIndex: 2, propertyName: \"Salary\" },\r\n { columnIndex: 3, propertyName: \"Details.Nickname\" },\r\n];\r\n\r\nfunction readPeople(ecdb: ECDb): Array<{ name: string, age: number, salary: number, nickname?: string }> {\r\n const rows: Array<{ name: string, age: number, salary: number, nickname?: string }> = [];\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n ecdb.withPreparedStatement(\"SELECT Name, Age, Salary, Details.Nickname FROM test.Person ORDER BY Name\", (stmt: ECSqlStatement) => {\r\n while (stmt.step() === DbResult.BE_SQLITE_ROW) {\r\n const nickname = stmt.getValue(3);\r\n rows.push({\r\n name: stmt.getValue(0).getString(),\r\n age: stmt.getValue(1).getInteger(),\r\n salary: stmt.getValue(2).getDouble(),\r\n nickname: nickname.isNull ? undefined : nickname.getString(),\r\n });\r\n }\r\n });\r\n return rows;\r\n}\r\n\r\ndescribe(\"ECDb CSV import\", () => {\r\n const outDir = KnownTestLocations.outputDir;\r\n\r\n it(\"imports in-memory CSV data atomically\", () => {\r\n using ecdb = ECDbTestHelper.createECDb(outDir, \"csv-import-data.ecdb\", testSchema);\r\n expect(ecdb.importCSVData([\r\n [\"Alice\", \"42\", \"1234.5\", \"Résumé\"],\r\n [\"用户\", \"31\", \"2500.25\", \"昵称\"],\r\n ], { className: \"Test.Person\", mapping })).to.equal(2);\r\n\r\n expect(() => ecdb.importCSVData([\r\n [\"Valid\", \"20\", \"1000\", \"valid\"],\r\n [\"Invalid\", \"not-an-integer\", \"1001\", \"invalid\"],\r\n ], { className: \"Test.Person\", mapping })).to.throw(/CSV data row 2 column 1/);\r\n\r\n expect(readPeople(ecdb)).to.deep.equal([\r\n { name: \"Alice\", age: 42, salary: 1234.5, nickname: \"Résumé\" },\r\n { name: \"用户\", age: 31, salary: 2500.25, nickname: \"昵称\" },\r\n ]);\r\n });\r\n\r\n it(\"streams a CSV file and rolls back malformed input\", () => {\r\n const csvPath = path.join(outDir, \"csv-import.csv\");\r\n fs.writeFileSync(csvPath, [\r\n \"Ignored,Age,Name,Salary,Nickname\",\r\n 'skip,42,\"Doe, Jane\",1234.5,\"said \"\"hello\"\"\\nnext\"',\r\n String.raw`skip,31,用户,2500.25,\\N`,\r\n ].join(\"\\r\\n\"));\r\n\r\n using ecdb = ECDbTestHelper.createECDb(outDir, \"csv-import-file.ecdb\", testSchema);\r\n const fileMapping = [\r\n { columnIndex: 1, propertyName: \"Age\" },\r\n { columnIndex: 2, propertyName: \"Name\" },\r\n { columnIndex: 3, propertyName: \"Salary\" },\r\n { columnIndex: 4, propertyName: \"Details.Nickname\" },\r\n ];\r\n expect(ecdb.importCSVFile(csvPath, {\r\n className: \"Test.Person\",\r\n mapping: fileMapping,\r\n hasHeader: true,\r\n nullValue: String.raw`\\N`,\r\n })).to.equal(2);\r\n\r\n fs.writeFileSync(csvPath, [\r\n \"skip,20,Valid,1000,valid\",\r\n 'skip,21,\"unterminated,1001,invalid',\r\n ].join(\"\\n\"));\r\n expect(() => ecdb.importCSVFile(csvPath, { className: \"Test.Person\", mapping: fileMapping }))\r\n .to.throw(/unterminated quoted field/);\r\n\r\n expect(readPeople(ecdb)).to.deep.equal([\r\n { name: \"Doe, Jane\", age: 42, salary: 1234.5, nickname: 'said \"hello\"\\nnext' },\r\n { name: \"用户\", age: 31, salary: 2500.25, nickname: undefined },\r\n ]);\r\n });\r\n\r\n it(\"rejects invalid column mappings before calling native code\", () => {\r\n using ecdb = ECDbTestHelper.createECDb(outDir, \"csv-import-mapping.ecdb\", testSchema);\r\n expect(() => ecdb.importCSVData([], { className: \"Test.Person\", mapping: [] })).to.throw(/must not be empty/);\r\n expect(() => ecdb.importCSVData([], {\r\n className: \"Test.Person\",\r\n mapping: [\r\n { columnIndex: 0, propertyName: \"Name\" },\r\n { columnIndex: 0, propertyName: \"Age\" },\r\n ],\r\n })).to.throw(/duplicate columnIndex/);\r\n });\r\n});\r\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DbResponseStatus } from "@itwin/core-common";
|
|
2
2
|
import { expect } from "chai";
|
|
3
|
+
import * as os from "os";
|
|
3
4
|
import { ConcurrentQuery } from "../../ConcurrentQuery";
|
|
4
5
|
import { SnapshotDb } from "../../IModelDb";
|
|
5
6
|
import { _nativeDb } from "../../core-backend";
|
|
@@ -9,6 +10,14 @@ async function delay(ms) {
|
|
|
9
10
|
setTimeout(resolve, ms);
|
|
10
11
|
});
|
|
11
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* The native default for `workerThreads` is 4, capped at the machine's hardware concurrency
|
|
15
|
+
* (2-core CI agents report 2). An explicit request above hardware concurrency is rejected and
|
|
16
|
+
* falls back to that default rather than being clamped.
|
|
17
|
+
*/
|
|
18
|
+
function defaultWorkerThreads() {
|
|
19
|
+
return Math.min(4, os.availableParallelism());
|
|
20
|
+
}
|
|
12
21
|
describe("ConcurrentQuery", () => {
|
|
13
22
|
it("default config", () => {
|
|
14
23
|
const testFile = IModelTestUtils.resolveAssetFile("test.bim");
|
|
@@ -24,7 +33,7 @@ describe("ConcurrentQuery", () => {
|
|
|
24
33
|
progressOpCount: 5000,
|
|
25
34
|
requestQueueSize: 2000,
|
|
26
35
|
statementCacheSizePerWorker: 40,
|
|
27
|
-
workerThreads:
|
|
36
|
+
workerThreads: defaultWorkerThreads(),
|
|
28
37
|
};
|
|
29
38
|
const config = ConcurrentQuery.resetConfig(db[_nativeDb], {});
|
|
30
39
|
expect(config).deep.eq(defaultConfig);
|
|
@@ -44,12 +53,20 @@ describe("ConcurrentQuery", () => {
|
|
|
44
53
|
progressOpCount: 6000,
|
|
45
54
|
requestQueueSize: 1000,
|
|
46
55
|
statementCacheSizePerWorker: 20,
|
|
47
|
-
workerThreads:
|
|
56
|
+
workerThreads: 1,
|
|
48
57
|
};
|
|
49
58
|
const config = ConcurrentQuery.resetConfig(db[_nativeDb], modifiedConfig);
|
|
50
59
|
expect(config).deep.eq(modifiedConfig);
|
|
51
60
|
db.close();
|
|
52
61
|
});
|
|
62
|
+
it("workerThreads above hardware concurrency falls back to the default", () => {
|
|
63
|
+
const testFile = IModelTestUtils.resolveAssetFile("test.bim");
|
|
64
|
+
const db = SnapshotDb.openFile(testFile);
|
|
65
|
+
const requested = os.availableParallelism() + 8;
|
|
66
|
+
const config = ConcurrentQuery.resetConfig(db[_nativeDb], { workerThreads: requested });
|
|
67
|
+
expect(config.workerThreads).eq(defaultWorkerThreads());
|
|
68
|
+
db.close();
|
|
69
|
+
});
|
|
53
70
|
it("time limit check", async () => {
|
|
54
71
|
const testFile = IModelTestUtils.resolveAssetFile("test.bim");
|
|
55
72
|
const db = SnapshotDb.openFile(testFile);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConcurrentQuery.test.js","sourceRoot":"","sources":["../../../../src/test/ecdb/ConcurrentQuery.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { DbQueryRequest, DbQueryResponse, DbResponseStatus } from \"@itwin/core-common\";\r\nimport { expect } from \"chai\";\r\nimport { ConcurrentQuery } from \"../../ConcurrentQuery\";\r\nimport { SnapshotDb } from \"../../IModelDb\";\r\nimport { _nativeDb } from \"../../core-backend\";\r\nimport { IModelTestUtils } from \"../IModelTestUtils\";\r\n\r\nasync function delay(ms: number): Promise<void> {\r\n return new Promise(resolve => {\r\n setTimeout(resolve, ms);\r\n });\r\n}\r\n\r\ndescribe(\"ConcurrentQuery\", () => {\r\n it(\"default config\", () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n const defaultConfig = {\r\n autoShutdownWhenIdleForSeconds: 1800,\r\n doNotUsePrimaryConnToPrepare: false,\r\n globalQuota: { time: 60, memory: 8388608 },\r\n ignoreDelay: true,\r\n ignorePriority: false,\r\n memoryMapFileSize: 0,\r\n monitorPollInterval: 5000,\r\n progressOpCount: 5000,\r\n requestQueueSize: 2000,\r\n statementCacheSizePerWorker: 40,\r\n workerThreads: 4,\r\n };\r\n const config = ConcurrentQuery.resetConfig(db[_nativeDb], {});\r\n expect(config).deep.eq(defaultConfig);\r\n db.close();\r\n });\r\n\r\n it(\"modify config\", () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n const modifiedConfig = {\r\n autoShutdownWhenIdleForSeconds: 100,\r\n doNotUsePrimaryConnToPrepare: true,\r\n globalQuota: { time: 10, memory: 1000000 },\r\n ignoreDelay: false,\r\n ignorePriority: true,\r\n memoryMapFileSize: 100,\r\n monitorPollInterval: 2000,\r\n progressOpCount: 6000,\r\n requestQueueSize: 1000,\r\n statementCacheSizePerWorker: 20,\r\n workerThreads: 3,\r\n };\r\n const config = ConcurrentQuery.resetConfig(db[_nativeDb], modifiedConfig);\r\n expect(config).deep.eq(modifiedConfig);\r\n db.close();\r\n });\r\n\r\n it(\"time limit check\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n ConcurrentQuery.resetConfig(db[_nativeDb], { globalQuota: { time: 1, memory: 100000 }, progressOpCount: 1000 });\r\n // await runSingleRequest(db, `SELECT 1`);\r\n const req: DbQueryRequest = {\r\n query: `WITH sequence(n,k) AS (\r\n SELECT 1,1 UNION ALL SELECT n + 1, random() FROM sequence WHERE n < 10000000\r\n ) SELECT COUNT(*) FROM sequence s`\r\n };\r\n\r\n const resp = await ConcurrentQuery.executeQueryRequest(db[_nativeDb], req);\r\n expect(resp.status).equals(DbResponseStatus.Partial);\r\n expect(resp.stats.timeLimit).equals(1000);\r\n expect(resp.stats.memLimit).equals(100000);\r\n expect(resp.stats.cpuTime).to.be.closeTo(1000970, 500000);\r\n expect(resp.stats.totalTime).to.be.closeTo(1001, 100);\r\n expect(resp.stats.memUsed).to.be.closeTo(2, 3);\r\n // prepareTime varies on CI: the first query prepares against the (cold) shared schema-source\r\n // connection, so assert only that it is negligible relative to the ~1000ms execution rather than\r\n // pinning it to a tight tolerance (which flakes -- e.g. observed 7ms against a 0 +/- 4 bound).\r\n expect(resp.stats.prepareTime).to.be.lessThan(100);\r\n db.close();\r\n });\r\n\r\n it(\"memory limit check\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n ConcurrentQuery.resetConfig(db[_nativeDb], { globalQuota: { time: 60, memory: 1000 }, progressOpCount: 1000 });\r\n // await runSingleRequest(db, `SELECT 1`);\r\n const req: DbQueryRequest = {\r\n query: `WITH sequence(n) AS (\r\n SELECT 1\r\n UNION ALL\r\n SELECT n + 1 FROM sequence WHERE n < 10000000\r\n )\r\n SELECT 'xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx' FROM sequence s`\r\n };\r\n\r\n const resp = await ConcurrentQuery.executeQueryRequest(db[_nativeDb], req);\r\n expect(resp.status).equals(DbResponseStatus.Partial);\r\n expect(resp.stats.timeLimit).equals(60000);\r\n expect(resp.stats.memLimit).equals(1000);\r\n expect(resp.stats.memUsed).to.be.closeTo(1037, 100);\r\n db.close();\r\n });\r\n\r\n it(\"prepare error\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n const req: DbQueryRequest = {\r\n query: `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`\r\n };\r\n\r\n const resp = await ConcurrentQuery.executeQueryRequest(db[_nativeDb], req);\r\n expect(resp.status).equals(DbResponseStatus.Error_ECSql_PreparedFailed);\r\n db.close();\r\n });\r\n\r\n it.skip(\"restart query #flaky\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n ConcurrentQuery.resetConfig(db[_nativeDb], { globalQuota: { time: 60, memory: 10000000 }, progressOpCount: 1000 });\r\n const req0: DbQueryRequest = {\r\n query: `WITH sequence(n) AS (\r\n SELECT 1 UNION ALL SELECT n + 1 FROM sequence WHERE n < 10000000\r\n ) SELECT n FROM sequence s`,\r\n restartToken: \"Blah\",\r\n };\r\n\r\n const req1: DbQueryRequest = {\r\n query: `WITH sequence(n) AS (\r\n SELECT 1 UNION ALL SELECT n + 1 FROM sequence WHERE n < 1000\r\n ) SELECT n FROM sequence s`,\r\n restartToken: \"Blah\",\r\n };\r\n\r\n const resp1 = ConcurrentQuery.executeQueryRequest(db[_nativeDb], req0);\r\n await delay(1);\r\n const resp2 = ConcurrentQuery.executeQueryRequest(db[_nativeDb], req1);\r\n const resp = await Promise.all([resp1, resp2]);\r\n expect(resp[0].status).equals(DbResponseStatus.Cancel); // can result in DbResponseStatus.Partial instead of DbResponseStatus.Cancel\r\n expect(resp[1].status).equals(DbResponseStatus.Done);\r\n db.close();\r\n });\r\n\r\n it(\"queue limit check\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n ConcurrentQuery.resetConfig(db[_nativeDb], { requestQueueSize: 40, globalQuota: { time: 5, memory: 100000 } });\r\n const req: DbQueryRequest = {\r\n query: `WITH sequence(n) AS (\r\n SELECT 1 UNION ALL SELECT n + 1 FROM sequence WHERE n < 10000000\r\n ) SELECT n FROM sequence s`,\r\n };\r\n\r\n const responsePromises: Promise<DbQueryResponse>[] = [];\r\n for (let i = 0; i < 60; ++i) {\r\n responsePromises.push(ConcurrentQuery.executeQueryRequest(db[_nativeDb], req));\r\n }\r\n const responses = await Promise.all(responsePromises);\r\n const queueResponses = Array.from(responses.filter((x) => x.status === DbResponseStatus.QueueFull));\r\n expect(queueResponses.length).to.be.greaterThanOrEqual(10);\r\n db.close();\r\n });\r\n\r\n it(\"timeout check\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n ConcurrentQuery.resetConfig(db[_nativeDb], { monitorPollInterval: 1, globalQuota: { time: 5, memory: 10000000 } });\r\n const req: DbQueryRequest = {\r\n query: `WITH sequence(n) AS (\r\n SELECT 1 UNION ALL SELECT n + 1 FROM sequence WHERE n < 10000000\r\n ) SELECT n FROM sequence s`,\r\n };\r\n\r\n const responsePromises: Promise<DbQueryResponse>[] = [];\r\n for (let i = 0; i < 100; ++i) {\r\n responsePromises.push(ConcurrentQuery.executeQueryRequest(db[_nativeDb], req));\r\n }\r\n const responses = await Promise.all(responsePromises);\r\n const queueResponses = Array.from(responses.filter((x) => x.status === DbResponseStatus.Timeout));\r\n expect(queueResponses.length).to.be.greaterThanOrEqual(10);\r\n db.close();\r\n });\r\n\r\n it(\"should handle concurrent queries during shutdown without deadlock\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const iModelDb = SnapshotDb.openFile(testFile);\r\n // Configure for maximum contention\r\n const config = {\r\n requestQueueSize: 1000,\r\n statementCacheSizePerWorker: 1, // Force frequent prepare calls\r\n };\r\n\r\n // Reset configuration\r\n ConcurrentQuery.resetConfig(iModelDb[_nativeDb], config);\r\n\r\n const responsePromises: Promise<DbQueryResponse>[] = [];\r\n const spamPromises: Promise<void>[] = [];\r\n let shouldStop = false;\r\n\r\n const spam = async () => {\r\n while (!shouldStop) {\r\n // Use random numbers to prevent query caching\r\n const query = `\r\n WITH sequence(n,k) AS (\r\n SELECT 1,1 UNION ALL SELECT n + 1, random() FROM sequence WHERE n < 10000000\r\n ) SELECT COUNT(*) FROM sequence s\r\n `;\r\n const request: DbQueryRequest = { query };\r\n const p = ConcurrentQuery.executeQueryRequest(iModelDb[_nativeDb], request);\r\n responsePromises.push(p);\r\n await new Promise(resolve => setImmediate(resolve));\r\n }\r\n };\r\n\r\n // Start spamming simple queries to increase contention\r\n spamPromises.push(spam());\r\n // Let queries start and establish contention\r\n await new Promise(resolve => setTimeout(resolve, 10));\r\n\r\n ConcurrentQuery.shutdown(iModelDb[_nativeDb]);\r\n\r\n shouldStop = true;\r\n\r\n // Wait for all promises to complete\r\n const results = await Promise.allSettled(responsePromises);\r\n await Promise.allSettled(spamPromises);\r\n\r\n const satisfiesShutDownResponse = results.some((result) => result.status === \"fulfilled\" && result.value.status === DbResponseStatus.ShuttingDown);\r\n\r\n expect(satisfiesShutDownResponse).to.be.true; // some queries should face shutdown\r\n\r\n // Restore original config by resetting to default\r\n ConcurrentQuery.resetConfig(iModelDb[_nativeDb], {});\r\n iModelDb.close();\r\n });\r\n});\r\n"]}
|
|
1
|
+
{"version":3,"file":"ConcurrentQuery.test.js","sourceRoot":"","sources":["../../../../src/test/ecdb/ConcurrentQuery.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { DbQueryRequest, DbQueryResponse, DbResponseStatus } from \"@itwin/core-common\";\r\nimport { expect } from \"chai\";\r\nimport * as os from \"os\";\r\nimport { ConcurrentQuery } from \"../../ConcurrentQuery\";\r\nimport { SnapshotDb } from \"../../IModelDb\";\r\nimport { _nativeDb } from \"../../core-backend\";\r\nimport { IModelTestUtils } from \"../IModelTestUtils\";\r\n\r\nasync function delay(ms: number): Promise<void> {\r\n return new Promise(resolve => {\r\n setTimeout(resolve, ms);\r\n });\r\n}\r\n\r\n/**\r\n * The native default for `workerThreads` is 4, capped at the machine's hardware concurrency\r\n * (2-core CI agents report 2). An explicit request above hardware concurrency is rejected and\r\n * falls back to that default rather than being clamped.\r\n */\r\nfunction defaultWorkerThreads(): number {\r\n return Math.min(4, os.availableParallelism());\r\n}\r\n\r\ndescribe(\"ConcurrentQuery\", () => {\r\n it(\"default config\", () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n const defaultConfig = {\r\n autoShutdownWhenIdleForSeconds: 1800,\r\n doNotUsePrimaryConnToPrepare: false,\r\n globalQuota: { time: 60, memory: 8388608 },\r\n ignoreDelay: true,\r\n ignorePriority: false,\r\n memoryMapFileSize: 0,\r\n monitorPollInterval: 5000,\r\n progressOpCount: 5000,\r\n requestQueueSize: 2000,\r\n statementCacheSizePerWorker: 40,\r\n workerThreads: defaultWorkerThreads(),\r\n };\r\n const config = ConcurrentQuery.resetConfig(db[_nativeDb], {});\r\n expect(config).deep.eq(defaultConfig);\r\n db.close();\r\n });\r\n\r\n it(\"modify config\", () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n const modifiedConfig = {\r\n autoShutdownWhenIdleForSeconds: 100,\r\n doNotUsePrimaryConnToPrepare: true,\r\n globalQuota: { time: 10, memory: 1000000 },\r\n ignoreDelay: false,\r\n ignorePriority: true,\r\n memoryMapFileSize: 100,\r\n monitorPollInterval: 2000,\r\n progressOpCount: 6000,\r\n requestQueueSize: 1000,\r\n statementCacheSizePerWorker: 20,\r\n workerThreads: 1,\r\n };\r\n const config = ConcurrentQuery.resetConfig(db[_nativeDb], modifiedConfig);\r\n expect(config).deep.eq(modifiedConfig);\r\n db.close();\r\n });\r\n\r\n it(\"workerThreads above hardware concurrency falls back to the default\", () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n const requested = os.availableParallelism() + 8;\r\n const config = ConcurrentQuery.resetConfig(db[_nativeDb], { workerThreads: requested });\r\n expect(config.workerThreads).eq(defaultWorkerThreads());\r\n db.close();\r\n });\r\n\r\n it(\"time limit check\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n ConcurrentQuery.resetConfig(db[_nativeDb], { globalQuota: { time: 1, memory: 100000 }, progressOpCount: 1000 });\r\n // await runSingleRequest(db, `SELECT 1`);\r\n const req: DbQueryRequest = {\r\n query: `WITH sequence(n,k) AS (\r\n SELECT 1,1 UNION ALL SELECT n + 1, random() FROM sequence WHERE n < 10000000\r\n ) SELECT COUNT(*) FROM sequence s`\r\n };\r\n\r\n const resp = await ConcurrentQuery.executeQueryRequest(db[_nativeDb], req);\r\n expect(resp.status).equals(DbResponseStatus.Partial);\r\n expect(resp.stats.timeLimit).equals(1000);\r\n expect(resp.stats.memLimit).equals(100000);\r\n expect(resp.stats.cpuTime).to.be.closeTo(1000970, 500000);\r\n expect(resp.stats.totalTime).to.be.closeTo(1001, 100);\r\n expect(resp.stats.memUsed).to.be.closeTo(2, 3);\r\n // prepareTime varies on CI: the first query prepares against the (cold) shared schema-source\r\n // connection, so assert only that it is negligible relative to the ~1000ms execution rather than\r\n // pinning it to a tight tolerance (which flakes -- e.g. observed 7ms against a 0 +/- 4 bound).\r\n expect(resp.stats.prepareTime).to.be.lessThan(100);\r\n db.close();\r\n });\r\n\r\n it(\"memory limit check\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n ConcurrentQuery.resetConfig(db[_nativeDb], { globalQuota: { time: 60, memory: 1000 }, progressOpCount: 1000 });\r\n // await runSingleRequest(db, `SELECT 1`);\r\n const req: DbQueryRequest = {\r\n query: `WITH sequence(n) AS (\r\n SELECT 1\r\n UNION ALL\r\n SELECT n + 1 FROM sequence WHERE n < 10000000\r\n )\r\n SELECT 'xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx' FROM sequence s`\r\n };\r\n\r\n const resp = await ConcurrentQuery.executeQueryRequest(db[_nativeDb], req);\r\n expect(resp.status).equals(DbResponseStatus.Partial);\r\n expect(resp.stats.timeLimit).equals(60000);\r\n expect(resp.stats.memLimit).equals(1000);\r\n expect(resp.stats.memUsed).to.be.closeTo(1037, 100);\r\n db.close();\r\n });\r\n\r\n it(\"prepare error\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n const req: DbQueryRequest = {\r\n query: `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`\r\n };\r\n\r\n const resp = await ConcurrentQuery.executeQueryRequest(db[_nativeDb], req);\r\n expect(resp.status).equals(DbResponseStatus.Error_ECSql_PreparedFailed);\r\n db.close();\r\n });\r\n\r\n it.skip(\"restart query #flaky\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n ConcurrentQuery.resetConfig(db[_nativeDb], { globalQuota: { time: 60, memory: 10000000 }, progressOpCount: 1000 });\r\n const req0: DbQueryRequest = {\r\n query: `WITH sequence(n) AS (\r\n SELECT 1 UNION ALL SELECT n + 1 FROM sequence WHERE n < 10000000\r\n ) SELECT n FROM sequence s`,\r\n restartToken: \"Blah\",\r\n };\r\n\r\n const req1: DbQueryRequest = {\r\n query: `WITH sequence(n) AS (\r\n SELECT 1 UNION ALL SELECT n + 1 FROM sequence WHERE n < 1000\r\n ) SELECT n FROM sequence s`,\r\n restartToken: \"Blah\",\r\n };\r\n\r\n const resp1 = ConcurrentQuery.executeQueryRequest(db[_nativeDb], req0);\r\n await delay(1);\r\n const resp2 = ConcurrentQuery.executeQueryRequest(db[_nativeDb], req1);\r\n const resp = await Promise.all([resp1, resp2]);\r\n expect(resp[0].status).equals(DbResponseStatus.Cancel); // can result in DbResponseStatus.Partial instead of DbResponseStatus.Cancel\r\n expect(resp[1].status).equals(DbResponseStatus.Done);\r\n db.close();\r\n });\r\n\r\n it(\"queue limit check\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n ConcurrentQuery.resetConfig(db[_nativeDb], { requestQueueSize: 40, globalQuota: { time: 5, memory: 100000 } });\r\n const req: DbQueryRequest = {\r\n query: `WITH sequence(n) AS (\r\n SELECT 1 UNION ALL SELECT n + 1 FROM sequence WHERE n < 10000000\r\n ) SELECT n FROM sequence s`,\r\n };\r\n\r\n const responsePromises: Promise<DbQueryResponse>[] = [];\r\n for (let i = 0; i < 60; ++i) {\r\n responsePromises.push(ConcurrentQuery.executeQueryRequest(db[_nativeDb], req));\r\n }\r\n const responses = await Promise.all(responsePromises);\r\n const queueResponses = Array.from(responses.filter((x) => x.status === DbResponseStatus.QueueFull));\r\n expect(queueResponses.length).to.be.greaterThanOrEqual(10);\r\n db.close();\r\n });\r\n\r\n it(\"timeout check\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const db = SnapshotDb.openFile(testFile);\r\n ConcurrentQuery.resetConfig(db[_nativeDb], { monitorPollInterval: 1, globalQuota: { time: 5, memory: 10000000 } });\r\n const req: DbQueryRequest = {\r\n query: `WITH sequence(n) AS (\r\n SELECT 1 UNION ALL SELECT n + 1 FROM sequence WHERE n < 10000000\r\n ) SELECT n FROM sequence s`,\r\n };\r\n\r\n const responsePromises: Promise<DbQueryResponse>[] = [];\r\n for (let i = 0; i < 100; ++i) {\r\n responsePromises.push(ConcurrentQuery.executeQueryRequest(db[_nativeDb], req));\r\n }\r\n const responses = await Promise.all(responsePromises);\r\n const queueResponses = Array.from(responses.filter((x) => x.status === DbResponseStatus.Timeout));\r\n expect(queueResponses.length).to.be.greaterThanOrEqual(10);\r\n db.close();\r\n });\r\n\r\n it(\"should handle concurrent queries during shutdown without deadlock\", async () => {\r\n const testFile = IModelTestUtils.resolveAssetFile(\"test.bim\");\r\n const iModelDb = SnapshotDb.openFile(testFile);\r\n // Configure for maximum contention\r\n const config = {\r\n requestQueueSize: 1000,\r\n statementCacheSizePerWorker: 1, // Force frequent prepare calls\r\n };\r\n\r\n // Reset configuration\r\n ConcurrentQuery.resetConfig(iModelDb[_nativeDb], config);\r\n\r\n const responsePromises: Promise<DbQueryResponse>[] = [];\r\n const spamPromises: Promise<void>[] = [];\r\n let shouldStop = false;\r\n\r\n const spam = async () => {\r\n while (!shouldStop) {\r\n // Use random numbers to prevent query caching\r\n const query = `\r\n WITH sequence(n,k) AS (\r\n SELECT 1,1 UNION ALL SELECT n + 1, random() FROM sequence WHERE n < 10000000\r\n ) SELECT COUNT(*) FROM sequence s\r\n `;\r\n const request: DbQueryRequest = { query };\r\n const p = ConcurrentQuery.executeQueryRequest(iModelDb[_nativeDb], request);\r\n responsePromises.push(p);\r\n await new Promise(resolve => setImmediate(resolve));\r\n }\r\n };\r\n\r\n // Start spamming simple queries to increase contention\r\n spamPromises.push(spam());\r\n // Let queries start and establish contention\r\n await new Promise(resolve => setTimeout(resolve, 10));\r\n\r\n ConcurrentQuery.shutdown(iModelDb[_nativeDb]);\r\n\r\n shouldStop = true;\r\n\r\n // Wait for all promises to complete\r\n const results = await Promise.allSettled(responsePromises);\r\n await Promise.allSettled(spamPromises);\r\n\r\n const satisfiesShutDownResponse = results.some((result) => result.status === \"fulfilled\" && result.value.status === DbResponseStatus.ShuttingDown);\r\n\r\n expect(satisfiesShutDownResponse).to.be.true; // some queries should face shutdown\r\n\r\n // Restore original config by resetting to default\r\n ConcurrentQuery.resetConfig(iModelDb[_nativeDb], {});\r\n iModelDb.close();\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
|