UNPKG

@itwin/core-backend

Version:
936 lines • 203 kB
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { if (value !== null && value !== void 0) { if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); var dispose, inner; if (async) { if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); dispose = value[Symbol.asyncDispose]; } if (dispose === void 0) { if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); dispose = value[Symbol.dispose]; if (async) inner = dispose; } if (typeof dispose !== "function") throw new TypeError("Object not disposable."); if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } }; env.stack.push({ value: value, dispose: dispose, async: async }); } else if (async) { env.stack.push({ async: true }); } return value; }; var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) { return function (env) { function fail(e) { env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; env.hasError = true; } var r, s = 0; function next() { while (r = env.stack.pop()) { try { if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next); if (r.dispose) { var result = r.dispose.call(r.value); if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); } else s |= 1; } catch (e) { fail(e); } } if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve(); if (env.hasError) throw env.error; } return next(); }; })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }); /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { assert } from "chai"; import { DbResult, Guid, Id64 } from "@itwin/core-bentley"; import { QueryBinder, QueryOptionsBuilder, QueryRowFormat } from "@itwin/core-common"; import { Point2d, Point3d, Range3d } from "@itwin/core-geometry"; import { _nativeDb, SnapshotDb } from "../../core-backend"; import { IModelTestUtils } from "../IModelTestUtils"; import { KnownTestLocations } from "../KnownTestLocations"; import { SequentialLogMatcher } from "../SequentialLogMatcher"; import { ECDbTestHelper } from "./ECDbTestHelper"; import { ConcurrentQuery } from "../../ConcurrentQuery"; /* eslint-disable @typescript-eslint/naming-convention */ const selectSingleRow = new QueryOptionsBuilder().setLimit({ count: 1, offset: -1 }).setRowFormat(QueryRowFormat.UseJsPropertyNames).getOptions(); async function query(ecdb, ecsql, params, config, callback) { ecdb.saveChanges(); let rowCount = 0; for await (const queryRow of ecdb.createQueryReader(ecsql, params, { ...config, rowFormat: QueryRowFormat.UseJsPropertyNames })) { rowCount++; if (callback) callback(queryRow.toRow()); } return rowCount; } async function queryRows(ecdb, ecsql, params, config) { ecdb.saveChanges(); const reader = ecdb.createQueryReader(ecsql, params, { ...config, rowFormat: QueryRowFormat.UseJsPropertyNames }); return reader.toArray(); } async function queryCount(ecdb, ecsql, params, config) { ecdb.saveChanges(); for await (const row of ecdb.createQueryReader(`SELECT COUNT(*) FROM (${ecsql})`, params, { ...config, rowFormat: QueryRowFormat.UseECSqlPropertyIndexes })) { return row[0]; } return -1; } function blobEqual(lhs, rhs) { if (!(lhs instanceof Uint8Array) || !(rhs instanceof Uint8Array)) throw new Error("expecting uint8array"); if (lhs.byteLength !== rhs.byteLength) return false; for (let i = 0; i < lhs.byteLength; i++) { if (lhs[i] !== rhs[i]) return false; } return true; } describe("ECSqlStatement", () => { const outDir = KnownTestLocations.outputDir; const testRange = new Range3d(1.2, 2.3, 3.4, 4.5, 5.6, 6.7); const blobVal = new Uint8Array(testRange.toFloat64Array().buffer); const abbreviatedBlobVal = `{"bytes":${blobVal.byteLength}}`; it("check asynchronous step and stepForInsert methods", async () => { const env_1 = { stack: [], error: void 0, hasError: false }; try { const ecdb = __addDisposableResource(env_1, ECDbTestHelper.createECDb(outDir, "asyncmethodtest.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"/> <ECProperty propertyName="dt" typeName="dateTime"/> <ECProperty propertyName="fooId" typeName="long" extendedTypeName="Id"/> </ECEntityClass> </ECSchema>`), false); assert.isTrue(ecdb.isOpen); const r = await ecdb.withCachedWriteStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(20,TIMESTAMP '2018-10-18T12:00:00Z',20)", async (stmt) => { return stmt.stepForInsert(); }); ecdb.saveChanges(); assert.equal(r.status, DbResult.BE_SQLITE_DONE); assert.equal(r.id, "0x1"); } catch (e_1) { env_1.error = e_1; env_1.hasError = true; } finally { __disposeResources(env_1); } }); it("concurrent query get meta data", async () => { const env_2 = { stack: [], error: void 0, hasError: false }; try { const ecdb = __addDisposableResource(env_2, ECDbTestHelper.createECDb(outDir, "asyncmethodtest.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"/> <ECProperty propertyName="dt" typeName="dateTime"/> <ECProperty propertyName="fooId" typeName="long" extendedTypeName="Id"/> </ECEntityClass> </ECSchema>`), false); assert.isTrue(ecdb.isOpen); await ecdb.withCachedWriteStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(20,TIMESTAMP '2018-10-18T12:00:00Z',20)", async (stmt) => { stmt.stepForInsert(); }); await ecdb.withCachedWriteStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(30,TIMESTAMP '2019-10-18T12:00:00Z',30)", async (stmt) => { stmt.stepForInsert(); }); ecdb.saveChanges(); const reader = ecdb.createQueryReader("SELECT * FROM ts.Foo"); let props = await reader.getMetaData(); assert.equal(props.length, 5); let rows = 0; while (await reader.step()) { rows++; } assert.equal(rows, 2); props = await reader.getMetaData(); assert.equal(props.length, 5); } catch (e_2) { env_2.error = e_2; env_2.hasError = true; } finally { __disposeResources(env_2); } }); it("null string accessor", async () => { const env_3 = { stack: [], error: void 0, hasError: false }; try { const ecdb = __addDisposableResource(env_3, ECDbTestHelper.createECDb(outDir, "nullstring.ecdb"), false); assert.isTrue(ecdb.isOpen); // eslint-disable-next-line @typescript-eslint/no-deprecated await ecdb.withPreparedStatement(`VALUES(NULL)`, async (stmt) => { stmt.step(); const str = stmt.getValue(0).getString(); assert.equal(str, ""); }); } catch (e_3) { env_3.error = e_3; env_3.hasError = true; } finally { __disposeResources(env_3); } }); it("should page results", async () => { const env_4 = { stack: [], error: void 0, hasError: false }; try { const ecdb = __addDisposableResource(env_4, ECDbTestHelper.createECDb(outDir, "pagingresultset.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>`), false); assert.isTrue(ecdb.isOpen); const ROW_COUNT = 27; // insert test rows for (let i = 1; i <= ROW_COUNT; i++) { const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(n) values(${i})`, async (stmt) => { return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); } ecdb.saveChanges(); for (let i = 1; i < ROW_COUNT; i++) { const rowCount = await queryCount(ecdb, "SELECT ECInstanceId, ECClassId, n FROM ts.Foo WHERE n <= ?", new QueryBinder().bindInt(1, i)); assert.equal(rowCount, i); } const temp = await queryRows(ecdb, "SELECT ECInstanceId FROM ONLY ts.Foo"); assert.equal(temp.length, ROW_COUNT); // query page by page const PAGE_SIZE = 5; const QUERY = "SELECT n FROM ts.Foo"; const EXPECTED_ROW_COUNT = [5, 5, 5, 5, 5, 2]; const ready = []; for (let i = 0; i < EXPECTED_ROW_COUNT.length; i++) { ready.push(queryRows(ecdb, QUERY, undefined, new QueryOptionsBuilder().setLimit({ offset: i * PAGE_SIZE, count: PAGE_SIZE }).getOptions())); } // verify if each page has right count of rows const results = await Promise.all(ready); for (let i = 0; i < EXPECTED_ROW_COUNT.length; i++) { assert.equal(results[i].length, EXPECTED_ROW_COUNT[i]); } } catch (e_4) { env_4.error = e_4; env_4.hasError = true; } finally { __disposeResources(env_4); } }); it("paging use cache statement queryRows()", async () => { const env_5 = { stack: [], error: void 0, hasError: false }; try { const ecdb = __addDisposableResource(env_5, ECDbTestHelper.createECDb(outDir, "pagingresultset.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>`), false); assert.isTrue(ecdb.isOpen); const ROW_COUNT = 100; // insert test rows for (let i = 1; i <= ROW_COUNT; i++) { const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(n) values(${i})`, async (stmt) => { return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); } ecdb.saveChanges(); // check if varying page number does not require prepare new statements ecdb.clearStatementCache(); const rca = await queryRows(ecdb, "SELECT count(*) as nRows FROM ts.Foo"); assert.equal(rca[0].nRows, 100); // expe const rc = await queryCount(ecdb, "SELECT * FROM ts.Foo"); assert.equal(rc, 100); // expe let rowNo = 0; for await (const row of ecdb.createQueryReader("SELECT * FROM ts.Foo", undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames })) { assert.equal(row.n, rowNo + 1); rowNo = rowNo + 1; } assert.equal(rowNo, 100); // expect all rows } catch (e_5) { env_5.error = e_5; env_5.hasError = true; } finally { __disposeResources(env_5); } }); it("should restart query", async () => { const env_6 = { stack: [], error: void 0, hasError: false }; try { const ecdb = __addDisposableResource(env_6, ECDbTestHelper.createECDb(outDir, "cancelquery.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>`), false); assert.isTrue(ecdb.isOpen); const ROW_COUNT = 100; // insert test rows for (let i = 1; i <= ROW_COUNT; i++) { const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(n) values(${i})`, async (stmt) => { return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); } ecdb.saveChanges(); ConcurrentQuery.resetConfig(ecdb[_nativeDb], { globalQuota: { time: 1 }, ignoreDelay: false }); let cancelled = 0; let successful = 0; let rowCount = 0; const scheduleQuery = async (delay) => { return new Promise(async (resolve, reject) => { try { const options = new QueryOptionsBuilder(); options.setDelay(delay); options.setRowFormat(QueryRowFormat.UseJsPropertyNames); options.setRestartToken("tag"); for await (const _row of ecdb.createQueryReader("SELECT * FROM ts.Foo", undefined, options.getOptions())) { rowCount++; } successful++; resolve(); } catch (err) { // we expect query to be cancelled if (err.errorNumber === DbResult.BE_SQLITE_INTERRUPT) { cancelled++; resolve(); } else { reject(new Error("rejected")); } } }); }; const queries = []; queries.push(scheduleQuery(5000)); queries.push(scheduleQuery(0)); await Promise.all(queries); // We expect at least one query to be cancelled assert.isAtLeast(cancelled, 1); assert.isAtLeast(successful, 1); assert.isAtLeast(rowCount, 1); } catch (e_6) { env_6.error = e_6; env_6.hasError = true; } finally { __disposeResources(env_6); } }); it("should use cache statement for query()", async () => { const env_7 = { stack: [], error: void 0, hasError: false }; try { const ecdb = __addDisposableResource(env_7, ECDbTestHelper.createECDb(outDir, "pagingresultset.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>`), false); assert.isTrue(ecdb.isOpen); const ROW_COUNT = 27; // insert test rows for (let i = 1; i <= ROW_COUNT; i++) { const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(n) values(${i})`, async (stmt) => { return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); } ecdb.saveChanges(); // check if varying page number does not require prepare new statements ecdb.clearStatementCache(); for (const _testPageSize of [1, 2, 4, 5, 6, 7, 10, ROW_COUNT]) { let rowNo = 1; for await (const row of ecdb.createQueryReader("SELECT n FROM ts.Foo WHERE n != ? and ECInstanceId < ?", new QueryBinder().bindInt(1, 123).bindInt(2, 30), { rowFormat: QueryRowFormat.UseJsPropertyNames })) { assert.equal(row.n, rowNo); rowNo = rowNo + 1; } assert.equal(rowNo, 28); // expect all rows assert.equal(0, ecdb.getCachedStatementCount()); // there must be single cached statement used with different size pages. } } catch (e_7) { env_7.error = e_7; env_7.hasError = true; } finally { __disposeResources(env_7); } }); it("concurrent query binding", async () => { const env_8 = { stack: [], error: void 0, hasError: false }; try { const ecdb = __addDisposableResource(env_8, ECDbTestHelper.createECDb(outDir, "pagingresultset.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>`), false); assert.isTrue(ecdb.isOpen); for (let i = 1; i <= 5; i++) { const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(n) values(${i})`, async (stmt) => { return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); } ecdb.saveChanges(); for await (const row of ecdb.createQueryReader("SELECT count(*) as cnt FROM ts.Foo WHERE n in (:a, :b, :c)", new QueryBinder().bindInt("a", 1).bindInt("b", 2).bindInt("c", 3), { rowFormat: QueryRowFormat.UseJsPropertyNames })) { assert.equal(row.cnt, 3); } for await (const row of ecdb.createQueryReader("SELECT count(*) as cnt FROM ts.Foo WHERE n in (?, ?, ?)", new QueryBinder().bindInt(1, 1).bindInt(2, 2).bindInt(3, 3), { rowFormat: QueryRowFormat.UseJsPropertyNames })) { assert.equal(row.cnt, 3); } const slm = new SequentialLogMatcher(); slm.append().error().category("ECDb").message("No parameter index found for parameter name: d."); try { for await (const row of ecdb.createQueryReader("SELECT count(*) as cnt FROM ts.Foo WHERE n in (:a, :b, :c)", new QueryBinder().bindInt("a", 1).bindInt("b", 2).bindInt("c", 3).bindInt("d", 3), { rowFormat: QueryRowFormat.UseJsPropertyNames })) { assert.equal(row.cnt, 3); } assert.isFalse(true); } catch (e) { assert.isNotNull(e); } assert.isTrue(slm.finishAndDispose()); } catch (e_8) { env_8.error = e_8; env_8.hasError = true; } finally { __disposeResources(env_8); } }); it("check HextoId() and IdToHex() ecsql functions", async () => { const env_9 = { stack: [], error: void 0, hasError: false }; try { const ecdb = __addDisposableResource(env_9, ECDbTestHelper.createECDb(outDir, "pagingresultset.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>`), false); assert.isTrue(ecdb.isOpen); for (let i = 1; i <= 2; i++) { const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(n) values(${i})`, async (stmt) => { return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); } ecdb.saveChanges(); for await (const row of ecdb.createQueryReader("SELECT IdToHex(ECInstanceId) as hexId, ECInstanceId, HexToId('0x1') as idhex FROM ts.Foo WHERE n = ?", new QueryBinder().bindInt(1, 1), { rowFormat: QueryRowFormat.UseJsPropertyNames })) { assert.equal(row.hexId, row.id); assert.equal(row.hexId, row.idhex); } } catch (e_9) { env_9.error = e_9; env_9.hasError = true; } finally { __disposeResources(env_9); } }); it("should bind BeGuid", async () => { const env_10 = { stack: [], error: void 0, hasError: false }; try { const ecdb = __addDisposableResource(env_10, ECDbTestHelper.createECDb(outDir, "pagingresultset.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="guid" typeName="binary" extendedTypeName="BeGuid"/> </ECEntityClass> </ECSchema>`), false); assert.isTrue(ecdb.isOpen); const maxRows = 10; const guids = []; for (let i = 0; i < maxRows; i++) { const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(guid) values(?)`, async (stmt) => { guids.push(Guid.createValue()); stmt.bindGuid(1, guids[i]); return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); } ecdb.saveChanges(); const uint8arrayToGuid = (guidArray) => { if (!(guidArray instanceof Uint8Array)) throw new Error("Expecting a Uint8Array type argument"); if (guidArray.byteLength !== 16) throw new Error("Expecting a Uint8Array of length 16"); let guidStr = ""; const part = [0, 4, 6, 8, 10, 16]; for (let z = 0; z < part.length - 1; z++) { guidArray.subarray(part[z], part[z + 1]).forEach((c) => { guidStr += (`00${c.toString(16)}`).slice(-2); }); if (z < part.length - 2) guidStr += "-"; } return guidStr; }; const guidToUint8Array = (v) => { if (v.length !== 36) throw new Error("Guid is expected to have 36 characters xxxxxxxx-xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"); const ar = new Uint8Array(16); const t = v.split("-").join(""); let i = 0; for (let z = 0; z < 32; z += 2) { ar[i++] = parseInt(t.substring(z, z + 2), 16); } return ar; }; const testGuid = "74da899a-6dde-406c-bf45-f4547d948f00"; assert.equal(testGuid, uint8arrayToGuid(guidToUint8Array(testGuid))); let k = 0; assert.equal(await query(ecdb, "SELECT guid FROM ts.Foo ORDER BY ECInstanceId", undefined, undefined, (row) => { assert.equal(row.guid, guids[k++]); }), maxRows); // following will not return any guid BLOB ? = STRING for (const guid of guids) { assert.equal(await query(ecdb, "SELECT guid FROM ts.Foo WHERE guid=?", new QueryBinder().bindString(1, guid), undefined, (row) => { assert.equal(row.guid, guid); }), 1); assert.equal(await query(ecdb, `SELECT guid FROM ts.Foo WHERE guid='${guid}'`, undefined, undefined, (row) => { assert.equal(row.guid, guid); }), 0); assert.equal(await query(ecdb, `SELECT guid FROM ts.Foo WHERE guid=StrToGuid('${guid}')`, undefined, undefined, (row) => { assert.equal(row.guid, guid); }), 1); assert.equal(await query(ecdb, "SELECT guid FROM ts.Foo WHERE guid=StrToGuid(?)", new QueryBinder().bindString(1, guid), undefined, (row) => { assert.equal(row.guid, guid); }), 1); assert.equal(await query(ecdb, "SELECT guid FROM ts.Foo WHERE GuidToStr(guid)=?", new QueryBinder().bindString(1, guid), undefined, (row) => { assert.equal(row.guid, guid); }), 1); assert.equal(await query(ecdb, "SELECT guid FROM ts.Foo WHERE guid=?", new QueryBinder().bindBlob(1, guidToUint8Array(guid)), undefined, (row) => { assert.equal(row.guid, guid); }), 1); assert.equal(await query(ecdb, "SELECT guid FROM ts.Foo WHERE guid=StrToGuid(?)", new QueryBinder().bindString(1, guid), undefined, (row) => { assert.equal(row.guid, guid); }), 1); assert.equal(await query(ecdb, "SELECT GuidToStr(guid) as gstr FROM ts.Foo WHERE guid=StrToGuid(?)", new QueryBinder().bindString(1, guid), undefined, (row) => { assert.equal(row.gstr, guid); }), 1); } } catch (e_10) { env_10.error = e_10; env_10.hasError = true; } finally { __disposeResources(env_10); } }); it("should bind Ids", async () => { const env_11 = { stack: [], error: void 0, hasError: false }; try { const ecdb = __addDisposableResource(env_11, ECDbTestHelper.createECDb(outDir, "bindids.ecdb"), false); assert.isTrue(ecdb.isOpen); const verify = async (ecdbToVerify, actualRes, expectedECInstanceId) => { if (!expectedECInstanceId) { assert.notEqual(actualRes.status, DbResult.BE_SQLITE_DONE); assert.isUndefined(actualRes.id); return; } assert.equal(actualRes.status, DbResult.BE_SQLITE_DONE); assert.isDefined(actualRes.id); assert.equal(actualRes.id, expectedECInstanceId); // eslint-disable-next-line @typescript-eslint/no-deprecated ecdbToVerify.withPreparedStatement("SELECT ECInstanceId, ECClassId, Name FROM ecdbf.ExternalFileInfo WHERE ECInstanceId=?", (stmt) => { stmt.bindId(1, expectedId); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const row = stmt.getRow(); assert.equal(row.id, expectedECInstanceId); assert.equal(row.className, "ECDbFileInfo.ExternalFileInfo"); assert.equal(row.name, `${Id64.getLocalId(expectedECInstanceId).toString()}.txt`); }); assert.equal(await query(ecdb, "SELECT ECInstanceId, ECClassId, Name FROM ecdbf.ExternalFileInfo WHERE ECInstanceId=?", new QueryBinder().bindString(1, expectedId), new QueryOptionsBuilder().setLimit({ count: 1, offset: -1 }).getOptions(), (row) => { assert.equal(row.id, expectedECInstanceId); assert.equal(row.className, "ECDbFileInfo.ExternalFileInfo"); assert.equal(row.name, `${Id64.getLocalId(expectedECInstanceId).toString()}.txt`); }), 1); }; let expectedId = Id64.fromLocalAndBriefcaseIds(4444, 0); let r = ecdb.withCachedWriteStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(?,?)", (stmt) => { stmt.bindId(1, expectedId); stmt.bindString(2, "4444.txt"); return stmt.stepForInsert(); }); await verify(ecdb, r, expectedId); expectedId = Id64.fromLocalAndBriefcaseIds(4445, 0); r = ecdb.withCachedWriteStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(:id,:name)", (stmt) => { stmt.bindId("id", expectedId); stmt.bindString("name", "4445.txt"); return stmt.stepForInsert(); }); await verify(ecdb, r, expectedId); expectedId = Id64.fromLocalAndBriefcaseIds(4446, 0); r = ecdb.withCachedWriteStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(?,?)", (stmt) => { stmt.bindValues([expectedId, "4446.txt"]); return stmt.stepForInsert(); }); await verify(ecdb, r, expectedId); expectedId = Id64.fromLocalAndBriefcaseIds(4447, 0); r = ecdb.withCachedWriteStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(:id,:name)", (stmt) => { stmt.bindValues({ id: expectedId, name: "4447.txt" }); return stmt.stepForInsert(); }); await verify(ecdb, r, expectedId); } catch (e_11) { env_11.error = e_11; env_11.hasError = true; } finally { __disposeResources(env_11); } }); it("should bind numeric and date strings", async () => { const env_12 = { stack: [], error: void 0, hasError: false }; try { const slm = new SequentialLogMatcher(); slm.append().error().category("ECDb").message("Type mismatch: only BindDateTime or BindText can be called for a column of the DateTime type."); slm.append().error().category("ECDb").message("Type mismatch: only BindDateTime or BindText can be called for a column of the DateTime type."); slm.append().error().category("ECDb").message("Type mismatch: only BindDateTime or BindText can be called for a column of the DateTime type."); slm.append().error().category("ECDb").message("Type mismatch: only BindDateTime or BindText can be called for a column of the DateTime type."); slm.append().error().category("ECDb").message("Type mismatch: only BindDateTime or BindText can be called for a column of the DateTime type."); slm.append().error().category("ECDb").message(/String must be a valid ISO 8601 date, time or timestamp/gm); slm.append().error().category("ECDb").message(/String must be a valid ISO 8601 date, time or timestamp/gm); slm.append().error().category("ECDb").message(/String must be a valid ISO 8601 date, time or timestamp/gm); slm.append().error().category("ECDb").message(/String must be a valid ISO 8601 date, time or timestamp/gm); slm.append().error().category("ECDb").message(/String must be a valid ISO 8601 date, time or timestamp/gm); slm.append().error().category("ECDb").message(/String must be a valid ISO 8601 date, time or timestamp/gm); slm.append().error().category("ECDb").message(/String must be a valid ISO 8601 date, time or timestamp/gm); slm.append().error().category("ECDb").message(/String must be a valid ISO 8601 date, time or timestamp/gm); slm.append().error().category("ECDb").message(/String must be a valid ISO 8601 date, time or timestamp/gm); slm.append().error().category("ECDb").message(/String must be a valid ISO 8601 date, time or timestamp/gm); slm.append().error().category("ECDb").message(/String must be a valid ISO 8601 date, time or timestamp/gm); slm.append().error().category("ECDb").message(/String must be a valid ISO 8601 date, time or timestamp/gm); slm.append().error().category("ECDb").message(/only BindDateTime or BindText can be called for a column of the DateTime type/gm); const ecdb = __addDisposableResource(env_12, ECDbTestHelper.createECDb(outDir, "bindnumericanddatestrings.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"/> <ECProperty propertyName="dt" typeName="dateTime"/> <ECProperty propertyName="fooId" typeName="long" extendedTypeName="Id"/> </ECEntityClass> </ECSchema>`), false); assert.isTrue(ecdb.isOpen); const r = ecdb.withCachedWriteStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(20,TIMESTAMP '2018-10-18T12:00:00Z',20)", (stmt) => { return stmt.stepForInsert(); }); ecdb.saveChanges(); assert.equal(r.status, DbResult.BE_SQLITE_DONE); const ecsqln = "SELECT 1 FROM ts.Foo WHERE n=?"; // eslint-disable-next-line @typescript-eslint/no-deprecated await ecdb.withPreparedStatement(ecsqln, async (stmt) => { const nNum = 20; const nStr = "20"; const nDt = "2019-01-21T12:00:00Z"; const nHexStr = "0x14"; stmt.bindInteger(1, nNum); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); assert.equal(await queryCount(ecdb, ecsqln, new QueryBinder().bindInt(1, nNum), selectSingleRow), 1); stmt.bindValue(1, nNum); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.getBinder(1).bind(nNum); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindValues([nNum]); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindString(1, nStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); assert.equal(await queryCount(ecdb, ecsqln, new QueryBinder().bindString(1, nStr), selectSingleRow), 1); stmt.bindValue(1, nStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.getBinder(1).bind(nStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindValues([nStr]); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindString(1, nDt); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE, "Date time string is not parsed. SQLite just converts it to something which does not match"); stmt.reset(); stmt.clearBindings(); assert.equal(await queryCount(ecdb, ecsqln, new QueryBinder().bindString(1, nDt), selectSingleRow), 0); stmt.bindValue(1, nDt); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE, "Date time string is not parsed. SQLite just converts it to something which does not match"); stmt.reset(); stmt.clearBindings(); stmt.getBinder(1).bind(nDt); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE, "Date time string is not parsed. SQLite just converts it to something which does not match"); stmt.reset(); stmt.clearBindings(); stmt.bindValues([nDt]); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE, "Date time string is not parsed. SQLite just converts it to something which does not match"); stmt.reset(); stmt.clearBindings(); stmt.bindString(1, nHexStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE, "Hex string is not parsed. SQLite just converts it to something which does not match"); stmt.reset(); stmt.clearBindings(); assert.equal(await queryCount(ecdb, ecsqln, new QueryBinder().bindString(1, nHexStr), selectSingleRow), 0); stmt.bindValue(1, nHexStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE, "Hex string is not parsed. SQLite just converts it to something which does not match"); stmt.reset(); stmt.clearBindings(); stmt.getBinder(1).bind(nHexStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE, "Hex string is not parsed. SQLite just converts it to something which does not match"); stmt.reset(); stmt.clearBindings(); stmt.bindValues([nHexStr]); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE, "Hex string is not parsed. SQLite just converts it to something which does not match"); stmt.reset(); stmt.clearBindings(); }); const ecsqldt = "SELECT 1 FROM ts.Foo WHERE dt=?"; // eslint-disable-next-line @typescript-eslint/no-deprecated await ecdb.withPreparedStatement(ecsqldt, async (stmt) => { const dtStr = "2018-10-18T12:00:00Z"; const num = 2458410; const str = "2458410"; const hexStr = "0x25832a"; stmt.bindDateTime(1, dtStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); assert.equal(await queryCount(ecdb, ecsqldt, new QueryBinder().bindString(1, dtStr)), 1); stmt.bindString(1, dtStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindValue(1, dtStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.getBinder(1).bind(dtStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindValues([dtStr]); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); assert.throw(() => stmt.bindInteger(1, num)); stmt.clearBindings(); assert.equal(await queryCount(ecdb, ecsqldt, QueryBinder.from([num])), 0); assert.throw(() => stmt.bindValue(1, num)); stmt.clearBindings(); assert.throw(() => stmt.getBinder(1).bind(num)); stmt.clearBindings(); assert.throw(() => stmt.bindValues([num])); stmt.clearBindings(); assert.throw(() => stmt.bindString(1, str)); stmt.clearBindings(); assert.throw(() => stmt.bindValue(1, str)); stmt.clearBindings(); assert.equal(await queryCount(ecdb, ecsqldt, QueryBinder.from([str])), 0); assert.throw(() => stmt.getBinder(1).bind(str)); stmt.clearBindings(); assert.throw(() => stmt.bindValues([str])); stmt.clearBindings(); assert.throw(() => stmt.bindString(1, hexStr)); stmt.clearBindings(); assert.equal(await queryCount(ecdb, ecsqldt, QueryBinder.from([hexStr])), 0); assert.throw(() => stmt.bindValue(1, hexStr)); stmt.clearBindings(); assert.throw(() => stmt.getBinder(1).bind(hexStr)); stmt.clearBindings(); assert.throw(() => stmt.bindValues([hexStr])); stmt.clearBindings(); }); const ecsqlfooId = "SELECT 1 FROM ts.Foo WHERE fooId=?"; // eslint-disable-next-line @typescript-eslint/no-deprecated await ecdb.withPreparedStatement(ecsqlfooId, async (stmt) => { const num = 20; const str = "20"; const dt = "2019-01-21T12:00:00Z"; const hexStr = "0x14"; stmt.bindId(1, hexStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); assert.equal(await queryCount(ecdb, ecsqldt, QueryBinder.from([hexStr])), 0); stmt.bindValues([hexStr]); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindString(1, hexStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindValue(1, hexStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.getBinder(1).bind(hexStr); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindValues([hexStr]); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindString(1, str); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); assert.equal(await queryCount(ecdb, ecsqldt, QueryBinder.from([str])), 0); stmt.bindValue(1, str); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.getBinder(1).bind(str); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindValues([str]); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindInteger(1, num); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); assert.equal(await queryCount(ecdb, ecsqldt, QueryBinder.from([num])), 0); stmt.bindValue(1, num); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.getBinder(1).bind(num); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindValues([num]); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); stmt.reset(); stmt.clearBindings(); stmt.bindString(1, dt); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE, "DateTime string is not parsed into what it means. SQlite just uses its regular string conversion routines which don't match here"); stmt.reset(); stmt.clearBindings(); assert.equal(await queryCount(ecdb, ecsqldt, QueryBinder.from([dt])), 0); stmt.bindValue(1, dt); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE, "DateTime string is not parsed into what it means. SQlite just uses its regular string conversion routines which don't match here"); stmt.reset(); stmt.clearBindings(); stmt.getBinder(1).bind(dt); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE, "DateTime string is not parsed into what it means. SQlite just uses its regular string conversion routines which don't match here"); stmt.reset(); stmt.clearBindings(); stmt.bindValues([dt]); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE, "DateTime string is not parsed into what it means. SQlite just uses its regular string conversion routines which don't match here"); stmt.reset(); stmt.clearBindings(); }); assert.isTrue(slm.finishAndDispose()); } catch (e_12) { env_12.error = e_12; env_12.hasError = true; } finally { __disposeResources(env_12); } }); it("should bind numbers", async () => { const env_13 = { stack: [], error: void 0, hasError: false }; try { const ecdb = __addDisposableResource(env_13, ECDbTestHelper.createECDb(outDir, "bindnumbers.ecdb", `<ECSchema schemaName="Test" alias="test" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.1"> <ECEntityClass typeName="Foo" modifier="Sealed"> <ECProperty propertyName="D" typeName="double"/> <ECProperty propertyName="I" typeName="int"/> <ECProperty propertyName="L" typeName="long"/> <ECProperty propertyName="S" typeName="string"/> <ECProperty propertyName="Description" typeName="string"/> </ECEntityClass> </ECSchema>`), false); assert.isTrue(ecdb.isOpen); const doubleVal = 3.5; let id = await ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindDouble')", async (stmt) => { stmt.bindDouble(1, doubleVal); stmt.bindDouble(2, doubleVal); stmt.bindDouble(3, doubleVal); stmt.bindDouble(4, doubleVal); const r = stmt.stepForInsert(); assert.equal(r.status, DbResult.BE_SQLITE_DONE); return r.id; }); // eslint-disable-next-line @typescript-eslint/no-deprecated await ecdb.withPreparedStatement("SELECT D,I,L,S FROM Test.Foo WHERE ECInstanceId=?", async (stmt) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const row = stmt.getRow(); assert.equal(row.d, doubleVal); assert.equal(row.i, 3); assert.equal(row.l, 3); assert.equal(row.s, "3.5"); }); assert.equal(await query(ecdb, "SELECT D,I,L,S FROM Test.Foo WHERE ECInstanceId=?", QueryBinder.from([id]), { limit: { count: 1 } }, (row) => { assert.equal(row.d, doubleVal); assert.equal(row.i, 3); assert.equal(row.l, 3); assert.equal(row.s, "3.5"); }), 1); const smallIntVal = 3; id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, small int')", (stmt) => { stmt.bindInteger(1, smallIntVal); stmt.bindInteger(2, smallIntVal); stmt.bindInteger(3, smallIntVal); stmt.bindInteger(4, smallIntVal); const r = stmt.stepForInsert(); assert.equal(r.status, DbResult.BE_SQLITE_DONE); return r.id; }); // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT D,I,L,S FROM Test.Foo WHERE ECInstanceId=?", (stmt) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const row = stmt.getRow(); assert.equal(row.d, smallIntVal); assert.equal(row.i, smallIntVal); assert.equal(row.l, smallIntVal); assert.equal(row.s, "3"); }); assert.equal(await query(ecdb, "SELECT D,I,L,S FROM Test.Foo WHERE ECInstanceId=?", QueryBinder.from([id]), selectSingleRow, (row) => { assert.equal(row.d, smallIntVal); assert.equal(row.i, smallIntVal); assert.equal(row.l, smallIntVal); assert.equal(row.s, "3"); }), 1); const largeUnsafeNumber = 12312312312312323654; // too large for int64, but fits into uint64 assert.isFalse(Number.isSafeInteger(largeUnsafeNumber)); const largeUnsafeNumberStr = "12312312312312323654"; const largeUnsafeNumberHexStr = "0xaade1ed08b0b5e46"; id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large unsafe number as string')", (stmt) => { stmt.bindInteger(1, largeUnsafeNumberStr); stmt.bindInteger(2, largeUnsafeNumberStr); stmt.bindInteger(3, largeUnsafeNumberStr); stmt.bi