diff --git a/common/changes/@itwin/core-backend/Soham-AddingTestsForIdSetVT_2024-10-14-13-05.json b/common/changes/@itwin/core-backend/Soham-AddingTestsForIdSetVT_2024-10-14-13-05.json new file mode 100644 index 000000000000..99b35bb89b62 --- /dev/null +++ b/common/changes/@itwin/core-backend/Soham-AddingTestsForIdSetVT_2024-10-14-13-05.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/core-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@itwin/core-backend" +} \ No newline at end of file diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 62566f58eb3c..3d7488ed0855 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: ../../core/backend: specifiers: - '@bentley/imodeljs-native': 4.11.15 + '@bentley/imodeljs-native': 4.11.16 '@itwin/build-tools': workspace:* '@itwin/cloud-agnostic-core': ^2.2.4 '@itwin/core-bentley': workspace:* @@ -62,7 +62,7 @@ importers: webpack: ^5.76.0 ws: ^7.5.10 dependencies: - '@bentley/imodeljs-native': 4.11.15 + '@bentley/imodeljs-native': 4.11.16 '@itwin/cloud-agnostic-core': 2.2.4_scz6qrwecfbbxg4vskopkl3a7u '@itwin/core-telemetry': link:../telemetry '@itwin/object-storage-azure': 2.2.5_scz6qrwecfbbxg4vskopkl3a7u @@ -3704,8 +3704,8 @@ packages: resolution: {integrity: sha512-IIs1wDcY2oZ8tJ3EZRw0U51M+0ZL3MvwoDYYmhUXaa9/UZqpFoOyLBGaxjirQteWXqTIMm3mFvmC+Nbn1ok4Iw==} dev: false - /@bentley/imodeljs-native/4.11.15: - resolution: {integrity: sha512-yuz4TLdRXD3Y0H4cKtvhxA9xcX9ARGqcS6uwNwtBWtcIqmbeIfnQEsdM7IM7oFYW/bRlbriqogFZbQ3y24/kjw==} + /@bentley/imodeljs-native/4.11.16: + resolution: {integrity: sha512-sGGQro7c0UW11aMVb/223rZvJr/9qWeby08e6NqbEx9YURmtnT1xIu4NAQ7PlxHJRzCCDAPssspCFJVK69BT+Q==} requiresBuild: true dev: false diff --git a/core/backend/package.json b/core/backend/package.json index 1ba948d72938..123d84bd6449 100644 --- a/core/backend/package.json +++ b/core/backend/package.json @@ -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", diff --git a/core/backend/src/test/ecdb/ECSqlQuery.test.ts b/core/backend/src/test/ecdb/ECSqlQuery.test.ts index 41bcbe9ec0b2..deaa3256507c 100644 --- a/core/backend/src/test/ecdb/ECSqlQuery.test.ts +++ b/core/backend/src/test/ecdb/ECSqlQuery.test.ts @@ -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(); diff --git a/core/backend/src/test/ecdb/ECSqlReader.test.ts b/core/backend/src/test/ecdb/ECSqlReader.test.ts index ea256d917e52..542623740d38 100644 --- a/core/backend/src/test/ecdb/ECSqlReader.test.ts +++ b/core/backend/src/test/ecdb/ECSqlReader.test.ts @@ -47,6 +47,45 @@ describe("ECSqlReader", (() => { }); }); + it("ecsql reader simple for IdSet", async () => { + await using(ECDbTestHelper.createECDb(outDir, "test.ecdb", + ` + + + + `), 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", + ` + + + + `), 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", ` diff --git a/core/backend/src/test/ecdb/ECSqlStatement.test.ts b/core/backend/src/test/ecdb/ECSqlStatement.test.ts index 3d7ba228f973..d67f7e70df8a 100644 --- a/core/backend/src/test/ecdb/ECSqlStatement.test.ts +++ b/core/backend/src/test/ecdb/ECSqlStatement.test.ts @@ -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 () => { diff --git a/core/backend/src/test/ecsql/queries/IdSetVTTests.ecsql.md b/core/backend/src/test/ecsql/queries/IdSetVTTests.ecsql.md new file mode 100644 index 000000000000..c2edf1a9da11 --- /dev/null +++ b/core/backend/src/test/ecsql/queries/IdSetVTTests.ecsql.md @@ -0,0 +1,586 @@ +Copyright © Bentley Systems, Incorporated. All rights reserved. See [LICENSE.md](../../../../LICENSE.md) for license terms and full copyright notice. + +# Testing returned values of IdSet virtual table + +- dataset: AllProperties.bim + +```sql +SELECT id from ECVLib.IdSet(?) ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +- bindIdSet 1, [0x15, 0x18, 0x19] + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| --------- | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | ------------------ | +| | id | false | 0 | id | id | Id | long | Id | id | + +| id | +| ---- | +| 0x15 | +| 0x18 | +| 0x19 | + +# Testing returned values of IdSet virtual table with alias + +- dataset: AllProperties.bim + +```sql +SELECT id a from ECVLib.IdSet(?) OPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +- bindIdSet 1, [0x15, 0x18, 0x19] + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| --------- | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | ------------------ | +| | a | true | 0 | a | a | Id | long | Id | id | + +| a | +| ---- | +| 0x15 | +| 0x18 | +| 0x19 | + +# Testing with hard coded json string with hex ids + +- dataset: AllProperties.bim + +```sql +SELECT i FROM aps.TestElement,ECVLib.IdSet('["0x15", "0x18", "0x19"]') where id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | ------------------ | +| AllProperties:IPrimitive | i | false | 0 | i | i | undefined | int | Int | i | + +| i | +| --- | +| 101 | +| 104 | +| 105 | + +# Testing with hard coded json string with decimal ids + +- dataset: AllProperties.bim + +```sql +SELECT i FROM aps.TestElement,ECVLib.IdSet('[21, 24, "25"]') where id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES = TRUE +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | ------------------ | +| AllProperties:IPrimitive | i | false | 0 | i | i | undefined | int | Int | i | + +| i | +| --- | +| 101 | +| 104 | +| 105 | + +# Testing INNER JOINS with IdSet + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT e.i FROM aps.TestElement e INNER JOIN ECVLib.IdSet(?) v ON e.ECInstanceId = v.id ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES = 1 +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | ------------------ | +| AllProperties:IPrimitive | i | false | 0 | i | i | undefined | int | Int | i | + +| i | +| --- | +| 101 | +| 104 | +| 105 | + +# Testing INNER JOINS with IdSet and also select VirtualProp + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + e.i, + v.id +FROM + aps.TestElement e + INNER JOIN ECVLib.IdSet (?) v ON v.id = e.ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES = true +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | ------------------ | +| AllProperties:IPrimitive | i | false | 0 | i | i | undefined | int | Int | i | +| | id | false | 1 | id | id | Id | long | Id | id | + +| i | id | +| --- | ---- | +| 101 | 0x15 | +| 104 | 0x18 | +| 105 | 0x19 | + +# Testing INNER JOIN with string prop + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + e.ECInstanceId, + e.s, + v.id +FROM + aps.TestElement e + JOIN ECVLib.IdSet (?) v ON e.ECInstanceId = v.id OPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ------------ | ------------ | -------- | ------ | ------------------ | +| | ECInstanceId | false | 0 | id | ECInstanceId | Id | long | Id | ECInstanceId | +| AllProperties:IPrimitive | s | false | 1 | s | s | undefined | string | String | s | +| | id | false | 2 | id_1 | id | Id | long | Id | id | + +| ECInstanceId | s | id | +| ------------ | ---- | ---- | +| 0x15 | str1 | 0x15 | +| 0x18 | str4 | 0x18 | +| 0x19 | str5 | 0x19 | + +# Testing LEFT OUTER JOIN on virtual table + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + e.ECInstanceId, + e.i, + v.id +FROM + ECVLib.IdSet (?) v + LEFT OUTER JOIN aps.TestElement e ON e.ECInstanceId = v.id ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ------------ | ------------ | -------- | ---- | ------------------ | +| | ECInstanceId | false | 0 | id | ECInstanceId | Id | long | Id | ECInstanceId | +| AllProperties:IPrimitive | i | false | 1 | i | i | undefined | int | Int | i | +| | id | false | 2 | id_1 | id | Id | long | Id | id | + +| ECInstanceId | i | id | +| ------------ | --- | ---- | +| 0x15 | 101 | 0x15 | +| 0x18 | 104 | 0x18 | +| 0x19 | 105 | 0x19 | + +# Testing LEFT OUTER JOIN on test table + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + e.ECInstanceId, + e.i, + v.id +FROM + aps.TestElement e + LEFT OUTER JOIN ECVLib.IdSet (?) v ON e.ECInstanceId = v.id OPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ------------ | ------------ | -------- | ---- | ------------------ | +| | ECInstanceId | false | 0 | id | ECInstanceId | Id | long | Id | ECInstanceId | +| AllProperties:IPrimitive | i | false | 1 | i | i | undefined | int | Int | i | +| | id | false | 2 | id_1 | id | Id | long | Id | id | + +| ECInstanceId | i | id | +| ------------ | --- | --------- | +| 0x14 | 100 | undefined | +| 0x15 | 101 | 0x15 | +| 0x16 | 102 | undefined | +| 0x17 | 103 | undefined | +| 0x18 | 104 | 0x18 | +| 0x19 | 105 | 0x19 | +| 0x1a | 106 | undefined | +| 0x1b | 107 | undefined | +| 0x1c | 108 | undefined | +| 0x1d | 109 | undefined | + +# Testing CROSS JOIN on test table + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + e.S, + e.i, + v.id +FROM + aps.TestElement e + CROSS JOIN ECVLib.IdSet (?) v ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ------ | ------------------ | +| AllProperties:IPrimitive | s | false | 0 | s | s | undefined | string | String | s | +| AllProperties:IPrimitive | i | false | 1 | i | i | undefined | int | Int | i | +| | id | false | 2 | id | id | Id | long | Id | id | + +| s | i | id | +| ---- | --- | ---- | +| str0 | 100 | 0x15 | +| str0 | 100 | 0x18 | +| str0 | 100 | 0x19 | +| str1 | 101 | 0x15 | +| str1 | 101 | 0x18 | +| str1 | 101 | 0x19 | +| str2 | 102 | 0x15 | +| str2 | 102 | 0x18 | +| str2 | 102 | 0x19 | +| str3 | 103 | 0x15 | +| str3 | 103 | 0x18 | +| str3 | 103 | 0x19 | +| str4 | 104 | 0x15 | +| str4 | 104 | 0x18 | +| str4 | 104 | 0x19 | +| str5 | 105 | 0x15 | +| str5 | 105 | 0x18 | +| str5 | 105 | 0x19 | +| str6 | 106 | 0x15 | +| str6 | 106 | 0x18 | +| str6 | 106 | 0x19 | +| str7 | 107 | 0x15 | +| str7 | 107 | 0x18 | +| str7 | 107 | 0x19 | +| str8 | 108 | 0x15 | +| str8 | 108 | 0x18 | +| str8 | 108 | 0x19 | +| str9 | 109 | 0x15 | +| str9 | 109 | 0x18 | +| str9 | 109 | 0x19 | + +# Testing FULL JOIN + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + e.s, + e.i, + v.id +FROM + aps.TestElement e + FULL JOIN ECVLib.IdSet (?) v ON e.ECInstanceId = v.id OPTIONS ENABLE_EXPERIMENTAL_FEATURES = TRUE +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ------ | ------------------ | +| AllProperties:IPrimitive | s | false | 0 | s | s | undefined | string | String | s | +| AllProperties:IPrimitive | i | false | 1 | i | i | undefined | int | Int | i | +| | id | false | 2 | id | id | Id | long | Id | id | + +| s | i | id | +| ---- | --- | --------- | +| str0 | 100 | undefined | +| str1 | 101 | 0x15 | +| str2 | 102 | undefined | +| str3 | 103 | undefined | +| str4 | 104 | 0x18 | +| str5 | 105 | 0x19 | +| str6 | 106 | undefined | +| str7 | 107 | undefined | +| str8 | 108 | undefined | +| str9 | 109 | undefined | + +# Testing NATURAL JOIN + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] +- errorDuringPrepare: true + +```sql +SELECT e.S, e.i, v.id FROM aps.TestElement e NATURAL JOIN ECVLib.IdSet(?) v ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES = True +``` + +# Testing JOIN + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + e.ECInstanceId, + e.i, + v.id +FROM + aps.TestElement e + JOIN ECVLib.IdSet (?) v ON e.ECInstanceId = v.id ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ------------ | ------------ | -------- | ---- | ------------------ | +| | ECInstanceId | false | 0 | id | ECInstanceId | Id | long | Id | ECInstanceId | +| AllProperties:IPrimitive | i | false | 1 | i | i | undefined | int | Int | i | +| | id | false | 2 | id_1 | id | Id | long | Id | id | + +| ECInstanceId | i | id | +| ------------ | --- | ---- | +| 0x15 | 101 | 0x15 | +| 0x18 | 104 | 0x18 | +| 0x19 | 105 | 0x19 | + +# Testing by binding with hex ids + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT i FROM aps.TestElement,ECVLib.IdSet(?) where id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | ------------------ | +| AllProperties:IPrimitive | i | false | 0 | i | i | undefined | int | Int | i | + +| i | +| --- | +| 101 | +| 104 | +| 105 | + +# Testing by binding with decimal ids for ECSql Statement + +- dataset: AllProperties.bim +- bindIdSet 1, [21, 24, 25] +- mode: Statement + +```sql +SELECT i FROM aps.TestElement,ECVLib.IdSet(?) where id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | ------------------ | +| AllProperties:IPrimitive | i | false | 0 | i | i | undefined | int | Int | i | + +| i | +| --- | +| 101 | +| 104 | +| 105 | + +# Testing by binding with decimal ids for ConcurrentQuery + +`The purpose of this test is to show that bindIdSet when working with ConcurrentQuery only takes into account hex ids and not decimal ids` + +- dataset: AllProperties.bim +- bindIdSet 1, [21, 24, 25] +- mode: ConcurrentQuery + +```sql +SELECT i FROM aps.TestElement,ECVLib.IdSet(?) where id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | originPropertyName | +| ------------------------ | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | ------------------ | +| AllProperties:IPrimitive | i | false | 0 | i | i | undefined | int | Int | i | + +| i | +| --- | + +# Testing IdSet following cte subquery + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + b +FROM + ( + WITH + cte (a, b) AS ( + SELECT ECInstanceId, i FROM aps.TestElement ) SELECT * FROM cte + ), + ECVLib.IdSet (?) +WHERE + id = a ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | +| --------- | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | +| | b | true | 0 | b | b | undefined | int | Int | + +| b | +| --- | +| 101 | +| 104 | +| 105 | + +# Testing cte subquery following IdSet + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + b +FROM +ECVLib.IdSet (?), + ( + WITH + cte (a, b) AS ( + SELECT ECInstanceId, i FROM aps.TestElement ) SELECT * FROM cte + ) +WHERE + id = a ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | +| --------- | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | +| | b | true | 0 | b | b | undefined | int | Int | + +| b | +| --- | +| 101 | +| 104 | +| 105 | + +# Testing nested CTE subquery following IdSet + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + b +FROM +ECVLib.IdSet (?), +( select * from ( + WITH + cte (a, b) AS ( + SELECT ECInstanceId, i FROM aps.TestElement ) SELECT * FROM cte + )) +WHERE + id = a OPTIONS ENABLE_EXPERIMENTAL_FEATURES = 1 +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | +| --------- | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | +| | b | true | 0 | b | b | undefined | int | Int | + +| b | +| --- | +| 101 | +| 104 | +| 105 | + +# Testing IdSet following nested CTE subquery + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + b +FROM +( select * from ( + WITH + cte (a, b) AS ( + SELECT ECInstanceId, i FROM aps.TestElement ) SELECT * FROM cte + )), +ECVLib.IdSet (?) +WHERE + id = a ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | type | +| --------- | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | ---- | +| | b | true | 0 | b | b | undefined | int | Int | + +| b | +| --- | +| 101 | +| 104 | +| 105 | + +# Testing IdSet following nested CTE without sub columns subquery + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + i +FROM +( select * from ( + WITH + cte AS ( + SELECT ECInstanceId, i FROM aps.TestElement ) SELECT * FROM cte + )), +ECVLib.IdSet (?) +WHERE + id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | +| ------------------------ | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | +| AllProperties:IPrimitive | i | false | 0 | i | i | undefined | int | + +| i | +| --- | +| 101 | +| 104 | +| 105 | + +# Testing CTE without sub columns subquery following IdSet + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] + +```sql +SELECT + i +FROM +ECVLib.IdSet (?), +( + WITH + cte AS ( + SELECT ECInstanceId, i FROM aps.TestElement ) SELECT * FROM cte + ) +WHERE + id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +| className | accessString | generated | index | jsonName | name | extendedType | typeName | +| ------------------------ | ------------ | --------- | ----- | -------- | ---- | ------------ | -------- | +| AllProperties:IPrimitive | i | false | 0 | i | i | undefined | int | + +| i | +| --- | +| 101 | +| 104 | +| 105 | + +# Testing IdSet by setting ENABLE_EXPERIMENTAL_FEATURES to false + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] +- errorDuringPrepare: true + +```sql +SELECT i FROM aps.TestElement,ECVLib.IdSet(?) where id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES = False +``` + +# Testing IdSet without ENABLE_EXPERIMENTAL_FEATURES + +- dataset: AllProperties.bim +- bindIdSet 1, [0x15, 0x18, 0x19] +- errorDuringPrepare: true + +```sql +SELECT i FROM aps.TestElement,ECVLib.IdSet(?) where id = ECInstanceId +``` diff --git a/core/backend/src/test/ecsql/queries/Misc.ecsql.md b/core/backend/src/test/ecsql/queries/Misc.ecsql.md index f2e9ff866eaf..b98f2e91ebd0 100644 --- a/core/backend/src/test/ecsql/queries/Misc.ecsql.md +++ b/core/backend/src/test/ecsql/queries/Misc.ecsql.md @@ -742,9 +742,9 @@ PRAGMA explain_query ( | | notused | true | 2 | notused | notused | undefined | long | Int64 | notused | | | detail | true | 3 | detail | detail | undefined | string | String | detail | -| id | parent | notused | detail | -| --- | ------ | ------- | ---------------------------------------------------------- | -| 3 | 0 | 0 | SEARCH main.ec_Class USING INDEX ix_ec_Class_Name (Name=?) | +| detail | +| ---------------------------------------------------------- | +| SEARCH main.ec_Class USING INDEX ix_ec_Class_Name (Name=?) | # Trying PRAGMA explain_query with cte @@ -761,11 +761,11 @@ PRAGMA explain_query ( [WITH cnt (x,y) AS ( SELECT 100, 200 ) SELEC | | notused | true | 2 | notused | notused | undefined | long | Int64 | notused | | | detail | true | 3 | detail | detail | undefined | string | String | detail | -| id | parent | notused | detail | -| --- | ------ | ------- | ----------------- | -| 2 | 0 | 0 | CO-ROUTINE cnt | -| 3 | 2 | 0 | SCAN CONSTANT ROW | -| 8 | 0 | 0 | SCAN cnt | +| detail | +| ----------------- | +| CO-ROUTINE cnt | +| SCAN CONSTANT ROW | +| SCAN cnt | # Trying PRAGMA explain_query with recursive cte @@ -790,14 +790,14 @@ PRAGMA explain_query ( | | notused | true | 2 | notused | notused | undefined | long | Int64 | notused | | | detail | true | 3 | detail | detail | undefined | string | String | detail | -| notused | detail | -| ------- | ----------------- | -| 0 | CO-ROUTINE cnt | -| 0 | SETUP | -| 0 | SCAN CONSTANT ROW | -| 0 | RECURSIVE STEP | -| 0 | SCAN cnt | -| 0 | SCAN cnt | +| detail | +| ----------------- | +| CO-ROUTINE cnt | +| SETUP | +| SCAN CONSTANT ROW | +| RECURSIVE STEP | +| SCAN cnt | +| SCAN cnt | # Using Scalar values in select clause with + operator diff --git a/docs/learning/ECSqlReference/IdSet.md b/docs/learning/ECSqlReference/IdSet.md new file mode 100644 index 000000000000..723b0398911b --- /dev/null +++ b/docs/learning/ECSqlReference/IdSet.md @@ -0,0 +1,47 @@ +# IdSet Virtual Table + +`IdSet` is an ECSQl built in virtual table which takes in a valid JSON array string of hex or decimal ids and stores the ids as a virtual table. It can be used as an alternative to `InVirtualSet`. The column retuned by `IdSet` virtual table will always be named `id` by default but can be aliased as per choice. `IdSet` virtual table is defined under the schema named `ECVLib`. It is an experimental feature, so the ECSql Option `ENABLE_EXPERIMENTAL_FEATURES` should be passed with the query in order for it to work. + +## Syntax + +```sql +SELECT i FROM aps.TestElement, ECVLib.IdSet('["0x15", "0x18", "0x19"]') where id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +OR + +```sql +SELECT i FROM aps.TestElement, ECVLib.IdSet(?) where id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +## Arguments accepted + +- `IdSet` accepts a valid string JSON array with valid string hex ids like `["0x15", "0x18", "0x19"]` +- `IdSet` also accepts a valid string JSON array with valid decimal ids like `[21, 24, 25]` +- `IdSet` also accepts a valid string JSON array with valid decimal ids being passed on as string like `["21", "24", "25"]` + +## BindIdSet support + +As `IdSet` is an alternative to `InVirtualSet()`, `bindIdSet` also works with `IdSet` virtual table + +```sql +SELECT i FROM aps.TestElement,ECVLib.IdSet(?) where id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +- bindIdSet 1, [0x15, 0x18, 0x19] + +## Migrating from `InVirtualSet` + +The following ECSql query using `InVirtualSet` + +```sql +SELECT i FROM aps.TestElement where InVirtualSet(?, ECInstanceId) +``` + +can be translated using `IdSet` as follows + +```sql +SELECT i FROM aps.TestElement,ECVLib.IdSet(?) where id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES +``` + +[ECSql Syntax](./index.md) diff --git a/docs/learning/ECSqlReference/index.md b/docs/learning/ECSqlReference/index.md index 37ffa0488a9d..aa0a8740f9e0 100644 --- a/docs/learning/ECSqlReference/index.md +++ b/docs/learning/ECSqlReference/index.md @@ -46,3 +46,4 @@ - [ECSQL Keywords](./ECSqlKeywords.md) - [Escaping keywords](./ECSqlKeywords.md#escaping-keywords) - [Views](./Views.md) +- [IdSet Virtual Table](./IdSet.md) diff --git a/test-apps/display-test-app/android/imodeljs-test-app/app/build.gradle b/test-apps/display-test-app/android/imodeljs-test-app/app/build.gradle index 0c745925798f..892f49d73b75 100644 --- a/test-apps/display-test-app/android/imodeljs-test-app/app/build.gradle +++ b/test-apps/display-test-app/android/imodeljs-test-app/app/build.gradle @@ -41,7 +41,7 @@ dependencies { implementation 'com.google.android.material:material:1.7.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.navigation:navigation-ui:2.5.3' - implementation 'com.github.itwin:mobile-native-android:4.11.15' + implementation 'com.github.itwin:mobile-native-android:4.11.16' implementation 'androidx.webkit:webkit:1.5.0' } diff --git a/test-apps/display-test-app/ios/imodeljs-test-app/imodeljs-test-app.xcodeproj/project.pbxproj b/test-apps/display-test-app/ios/imodeljs-test-app/imodeljs-test-app.xcodeproj/project.pbxproj index 0077dd52fe14..d4ab4fa6d668 100644 --- a/test-apps/display-test-app/ios/imodeljs-test-app/imodeljs-test-app.xcodeproj/project.pbxproj +++ b/test-apps/display-test-app/ios/imodeljs-test-app/imodeljs-test-app.xcodeproj/project.pbxproj @@ -453,7 +453,7 @@ repositoryURL = "https://github.com/iTwin/mobile-native-ios"; requirement = { kind = exactVersion; - version = 4.11.15; + version = 4.11.16; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/tools/internal/ios/core-test-runner/core-test-runner.xcodeproj/project.pbxproj b/tools/internal/ios/core-test-runner/core-test-runner.xcodeproj/project.pbxproj index fe34f121bc49..fea1b3ba0da4 100644 --- a/tools/internal/ios/core-test-runner/core-test-runner.xcodeproj/project.pbxproj +++ b/tools/internal/ios/core-test-runner/core-test-runner.xcodeproj/project.pbxproj @@ -552,7 +552,7 @@ repositoryURL = "https://github.com/iTwin/mobile-native-ios"; requirement = { kind = exactVersion; - version = 4.11.15; + version = 4.11.16; }; }; /* End XCRemoteSwiftPackageReference section */