@itwin/core-backend
Version:
iTwin.js backend components
936 lines • 203 kB
JavaScript
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