Skip to content

Commit

Permalink
Added tests cases for IdSet VirtualTable (backport #7260) [release/4.…
Browse files Browse the repository at this point in the history
…11.x] (#7648)

Co-authored-by: Soham Bhattacharjee <[email protected]>
Co-authored-by: imodeljs-admin <[email protected]>
  • Loading branch information
3 people authored Feb 6, 2025
1 parent 6379df9 commit 98e4e6c
Show file tree
Hide file tree
Showing 13 changed files with 887 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/core-backend",
"comment": "",
"type": "none"
}
],
"packageName": "@itwin/core-backend"
}
8 changes: 4 additions & 4 deletions common/config/rush/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion core/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
"webpack": "^5.76.0"
},
"dependencies": {
"@bentley/imodeljs-native": "4.11.15",
"@bentley/imodeljs-native": "4.11.16",
"@itwin/cloud-agnostic-core": "^2.2.4",
"@itwin/core-telemetry": "workspace:*",
"@itwin/object-storage-azure": "^2.2.5",
Expand Down
63 changes: 63 additions & 0 deletions core/backend/src/test/ecdb/ECSqlQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,69 @@ describe("ECSql Query", () => {
assert.isTrue(reader.stats.backendCpuTime > 0);
assert.isTrue(reader.stats.backendMemUsed > 100);
});
it("concurrent query bind idset in IdSet virtual table", async () => {
const ids: string[] = [];
for await (const row of imodel1.createQueryReader("SELECT ECInstanceId FROM BisCore.Element LIMIT 23")) {
ids.push(row[0]);
}
const reader = imodel1.createQueryReader("SELECT * FROM BisCore.element, ECVLib.IdSet(?) WHERE id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES", QueryBinder.from([ids]));
let props = await reader.getMetaData();
assert.equal(props.length, 12); // 11 for BisCore.element and 1 for IdSet
let rows = 0;
while (await reader.step()) {
rows++;
}
assert.equal(rows, 23);
props = await reader.getMetaData();
assert.equal(props.length, 12); // 11 for BisCore.element and 1 for IdSet
assert.equal(reader.stats.backendRowsReturned, 23);
assert.isTrue(reader.stats.backendCpuTime > 0);
assert.isTrue(reader.stats.backendMemUsed > 100);
});
it("concurrent query bind single id in IdSet virtual table", async () => {
let ids: string = "";
for await (const row of imodel1.createQueryReader("SELECT ECInstanceId FROM BisCore.Element LIMIT 23")) {
ids = row[0]; // getting only the first id
break;
}
const reader = imodel1.createQueryReader("SELECT * FROM BisCore.element, ECVLib.IdSet(?) WHERE id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES", QueryBinder.from([ids]));
let props = await reader.getMetaData();
assert.equal(props.length, 12); // 11 for BisCore.element and 1 for IdSet
let rows = 0; // backend will fail to bind so no rows will be returned
while (await reader.step()) {
rows++;
}
assert.equal(rows, 0);
props = await reader.getMetaData();
assert.equal(props.length, 12); // 11 for BisCore.element and 1 for IdSet
assert.equal(reader.stats.backendRowsReturned, 0);
assert.isTrue(reader.stats.backendCpuTime > 0);
});
it("concurrent query bind idset with invalid values in IdSet virtual table", async () => {
const ids: string[] = ["0x1","ABC","YZ"];

const reader = imodel1.createQueryReader("SELECT * FROM BisCore.element, ECVLib.IdSet(?) WHERE id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES", QueryBinder.from([ids]));
let props = await reader.getMetaData();
assert.equal(props.length, 12); // 11 for BisCore.element and 1 for IdSet
let rows = 0; // backend will bind successfully but some of the values are not valid for IdSet VT so those values will be ignored
while (await reader.step()) {
rows++;
}
assert.equal(rows, 1);
props = await reader.getMetaData();
assert.equal(props.length, 12); // 11 for BisCore.element and 1 for IdSet
assert.equal(reader.stats.backendRowsReturned, 1);
assert.isTrue(reader.stats.backendCpuTime > 0);
});
it("concurrent query bind idset with invalid values in IdSet virtual table", async () => {
const ids: string[] = ["ABC", "0x1","YZ"]; // as first value is not an Id so QueryBinder.from will throw error of "unsupported type"

try{
imodel1.createQueryReader("SELECT * FROM BisCore.element, ECVLib.IdSet(?) WHERE id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES", QueryBinder.from([ids]));
}catch(err: any){
assert.equal(err.message, "unsupported type");
}
});
it("concurrent query get meta data", async () => {
const reader = imodel1.createQueryReader("SELECT * FROM BisCore.element");
let props = await reader.getMetaData();
Expand Down
39 changes: 39 additions & 0 deletions core/backend/src/test/ecdb/ECSqlReader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,45 @@ describe("ECSqlReader", (() => {
});
});

it("ecsql reader simple for IdSet", async () => {
await using(ECDbTestHelper.createECDb(outDir, "test.ecdb",
`<ECSchema schemaName="Test" alias="ts" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
<ECEntityClass typeName="Foo" modifier="Sealed">
<ECProperty propertyName="n" typeName="int"/>
</ECEntityClass>
</ECSchema>`), async (ecdb: ECDb) => {
assert.isTrue(ecdb.isOpen);
ecdb.saveChanges();
const params = new QueryBinder();
params.bindIdSet(1, ["0x32"]);
const optionBuilder = new QueryOptionsBuilder();
optionBuilder.setRowFormat(QueryRowFormat.UseJsPropertyNames);
reader = ecdb.createQueryReader("SELECT ECInstanceId, Name FROM meta.ECClassDef, ECVLib.IdSet(?) WHERE id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES", params, optionBuilder.getOptions());
const rows = await reader.toArray();
assert.equal(rows[0].id, "0x32");
assert.equal(rows.length, 1);
});
});

it("bindIdSet not working with integer Ids", async () => {
await using(ECDbTestHelper.createECDb(outDir, "test.ecdb",
`<ECSchema schemaName="Test" alias="ts" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
<ECEntityClass typeName="Foo" modifier="Sealed">
<ECProperty propertyName="n" typeName="int"/>
</ECEntityClass>
</ECSchema>`), async (ecdb: ECDb) => {
assert.isTrue(ecdb.isOpen);
ecdb.saveChanges();
const params = new QueryBinder();
params.bindIdSet(1, ["50"]);
const optionBuilder = new QueryOptionsBuilder();
optionBuilder.setRowFormat(QueryRowFormat.UseJsPropertyNames);
reader = ecdb.createQueryReader("SELECT ECInstanceId, Name FROM meta.ECClassDef WHERE InVirtualSet(?, ECInstanceId)", params, optionBuilder.getOptions());
const rows = await reader.toArray();
assert.equal(rows.length, 0);
});
});

it("ecsql reader simple using query reader", async () => {
await using(ECDbTestHelper.createECDb(outDir, "test.ecdb",
`<ECSchema schemaName="Test" alias="ts" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
Expand Down
117 changes: 117 additions & 0 deletions core/backend/src/test/ecdb/ECSqlStatement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1839,6 +1839,123 @@ describe("ECSqlStatement", () => {
});
});

it("should bind IdSets to IdSet Virtual Table", async () => {
await using(ECDbTestHelper.createECDb(outDir, "bindids.ecdb"), async (ecdb: ECDb) => {
assert.isTrue(ecdb.isOpen);

const idNumbers: number[] = [4444, 4545, 1234, 6758, 1312];
ecdb.withPreparedStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(?,?)", (stmt: ECSqlStatement) => {
idNumbers.forEach((idNum: number) => {
const expectedId = Id64.fromLocalAndBriefcaseIds(idNum, 0);
stmt.bindId(1, expectedId);
stmt.bindString(2, `${idNum}.txt`);
const r: ECSqlInsertResult = stmt.stepForInsert();
assert.equal(r.status, DbResult.BE_SQLITE_DONE);
assert.isDefined(r.id);
assert.equal(r.id!, expectedId);
ecdb.saveChanges();

ecdb.withStatement(`SELECT ECInstanceId, ECClassId, Name FROM ecdbf.ExternalFileInfo WHERE ECInstanceId=${expectedId}`, (confstmt: ECSqlStatement) => {
assert.equal(confstmt.step(), DbResult.BE_SQLITE_ROW);
const row = confstmt.getRow();
assert.equal(row.id, expectedId);
assert.equal(row.className, "ECDbFileInfo.ExternalFileInfo");
assert.equal(row.name, `${Id64.getLocalId(expectedId).toString()}.txt`);
});
stmt.reset();
stmt.clearBindings();
});
});

ecdb.withPreparedStatement("SELECT ECInstanceId, ECClassId, Name from ecdbf.ExternalFileInfo, ECVLib.IdSet(?) WHERE id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES", (stmt: ECSqlStatement) => {
let idSet: Id64String[] = [];
stmt.bindIdSet(1, idSet);
let result = stmt.step();
assert.equal(result, DbResult.BE_SQLITE_DONE);
stmt.reset();
stmt.clearBindings();

idSet = [Id64.fromLocalAndBriefcaseIds(idNumbers[2], 0)];
stmt.bindIdSet(1, idSet);
result = stmt.step();
assert.equal(result, DbResult.BE_SQLITE_ROW);
let row = stmt.getRow();
assert.equal(row.name, `${idNumbers[2]}.txt`);
stmt.reset();
stmt.clearBindings();

idSet.push(idNumbers[0].toString());
stmt.bindIdSet(1, idSet);
result = stmt.step();
assert.equal(result, DbResult.BE_SQLITE_ROW);
row = stmt.getRow();
assert.equal(row.name, `${idNumbers[2]}.txt`);
result = stmt.step();
assert.equal(result, DbResult.BE_SQLITE_ROW);
row = stmt.getRow();
assert.equal(row.name, `${idNumbers[0]}.txt`);
});
});
});

it("Error Checking For binding to IdSet statements", async () => {
await using(ECDbTestHelper.createECDb(outDir, "bindids.ecdb"), async (ecdb: ECDb) => {
assert.isTrue(ecdb.isOpen);

const idNumbers: number[] = [4444, 4545, 1234, 6758, 1312];
ecdb.withPreparedStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(?,?)", (stmt: ECSqlStatement) => {
idNumbers.forEach((idNum: number) => {
const expectedId = Id64.fromLocalAndBriefcaseIds(idNum, 0);
stmt.bindId(1, expectedId);
stmt.bindString(2, `${idNum}.txt`);
const r: ECSqlInsertResult = stmt.stepForInsert();
assert.equal(r.status, DbResult.BE_SQLITE_DONE);
assert.isDefined(r.id);
assert.equal(r.id!, expectedId);
ecdb.saveChanges();

ecdb.withStatement(`SELECT ECInstanceId, ECClassId, Name FROM ecdbf.ExternalFileInfo WHERE ECInstanceId=${expectedId}`, (confstmt: ECSqlStatement) => {
assert.equal(confstmt.step(), DbResult.BE_SQLITE_ROW);
const row = confstmt.getRow();
assert.equal(row.id, expectedId);
assert.equal(row.className, "ECDbFileInfo.ExternalFileInfo");
assert.equal(row.name, `${Id64.getLocalId(expectedId).toString()}.txt`);
});
stmt.reset();
stmt.clearBindings();
});
});

ecdb.withPreparedStatement("SELECT ECInstanceId, ECClassId, Name from ecdbf.ExternalFileInfo, ECVLib.IdSet(?) WHERE id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES", (stmt: ECSqlStatement) => {
let idSet: Id64String[] = [];
stmt.bindIdSet(1, idSet);
let result = stmt.step();
assert.equal(result, DbResult.BE_SQLITE_DONE);
stmt.reset();
stmt.clearBindings();

idSet = ["0X1","ABC"];
try{
stmt.bindIdSet(1, idSet);
}catch(err: any){
assert.equal(err.message, "Error binding id set");
}
result = stmt.step();
assert.equal(result, DbResult.BE_SQLITE_DONE);
stmt.reset();
stmt.clearBindings();

try{
stmt.bindId(1, idNumbers[0].toString());
}catch(err: any){
assert.equal(err.message, "Error binding Id");
}
result = stmt.step();
assert.equal(result, DbResult.BE_SQLITE_DONE);
});
});
});

/* This test doesn't do anything specific with the binder life time but just runs a few scenarios
with and without statement cache to test that stuff works fine */
it("check ECSqlBinder life time", async () => {
Expand Down
Loading

0 comments on commit 98e4e6c

Please sign in to comment.