diff --git a/common/api/core-backend.api.md b/common/api/core-backend.api.md index 7a70357ce3d1..1cd1e04790aa 100644 --- a/common/api/core-backend.api.md +++ b/common/api/core-backend.api.md @@ -1800,7 +1800,10 @@ export class ECDb implements Disposable { openDb(pathName: string, openMode?: ECDbOpenMode): void; // @internal prepareSqliteStatement(sql: string, logErrors?: boolean): SqliteStatement; + // @deprecated prepareStatement(ecsql: string, logErrors?: boolean): ECSqlStatement; + // @beta + prepareWriteStatement(ecsql: string, logErrors?: boolean): ECSqlWriteStatement; // @deprecated query(ecsql: string, params?: QueryBinder, options?: QueryOptions): AsyncIterableIterator; // @deprecated @@ -1810,10 +1813,16 @@ export class ECDb implements Disposable { // @deprecated restartQuery(token: string, ecsql: string, params?: QueryBinder, options?: QueryOptions): AsyncIterableIterator; saveChanges(changesetName?: string): void; + // @beta + withCachedWriteStatement(ecsql: string, callback: (stmt: ECSqlWriteStatement) => T, logErrors?: boolean): T; withPreparedSqliteStatement(sql: string, callback: (stmt: SqliteStatement) => T, logErrors?: boolean): T; + // @deprecated withPreparedStatement(ecsql: string, callback: (stmt: ECSqlStatement) => T, logErrors?: boolean): T; withSqliteStatement(sql: string, callback: (stmt: SqliteStatement) => T, logErrors?: boolean): T; + // @deprecated withStatement(ecsql: string, callback: (stmt: ECSqlStatement) => T, logErrors?: boolean): T; + // @beta + withWriteStatement(ecsql: string, callback: (stmt: ECSqlWriteStatement) => T, logErrors?: boolean): T; } // @public @@ -1825,7 +1834,7 @@ export enum ECDbOpenMode { ReadWrite = 1 } -// @public +// @public @deprecated export interface ECEnumValue { // (undocumented) key: string; @@ -1875,7 +1884,7 @@ export class ECSqlBinder { bindStruct(val: object): void; } -// @public +// @public @deprecated export interface ECSqlColumnInfo { getAccessString(): string; getOriginPropertyName(): string | undefined; @@ -1904,7 +1913,7 @@ export interface ECSqlRowArg { rowFormat?: QueryRowFormat; } -// @public +// @public @deprecated export class ECSqlStatement implements IterableIterator, Disposable { [Symbol.dispose](): void; [Symbol.iterator](): IterableIterator; @@ -1954,7 +1963,7 @@ export class ECSqlStatement implements IterableIterator, Disposable { }; } -// @public +// @public @deprecated export class ECSqlValue { // @internal constructor(val: IModelJsNative.ECSqlValue); @@ -1981,7 +1990,7 @@ export class ECSqlValue { get value(): any; } -// @public +// @public @deprecated export class ECSqlValueIterator implements IterableIterator { // (undocumented) [Symbol.iterator](): IterableIterator; @@ -1991,6 +2000,49 @@ export class ECSqlValueIterator implements IterableIterator { next(): IteratorResult; } +// @public +export class ECSqlWriteStatement { + constructor(stmt?: ECSqlStatement); + bindArray(parameter: number | string, val: any[]): void; + bindBlob(parameter: number | string, blob: string | Uint8Array | ArrayBuffer | SharedArrayBuffer): void; + bindBoolean(parameter: number | string, val: boolean): void; + bindDateTime(parameter: number | string, isoDateTimeString: string): void; + bindDouble(parameter: number | string, val: number): void; + bindGuid(parameter: number | string, val: GuidString): void; + bindId(parameter: number | string, val: Id64String): void; + // (undocumented) + bindIdSet(parameter: number | string, val: Id64String[]): void; + bindInteger(parameter: number | string, val: number | string): void; + bindNavigation(parameter: number | string, val: NavigationBindingValue): void; + bindNull(parameter: number | string): void; + bindPoint2d(parameter: number | string, val: XAndY): void; + bindPoint3d(parameter: number | string, val: XYAndZ): void; + bindRange3d(parameter: number | string, val: LowAndHighXYZ): void; + bindString(parameter: number | string, val: string): void; + bindStruct(parameter: number | string, val: object): void; + bindValue(parameter: number | string, val: any): void; + bindValues(values: any[] | object): void; + clearBindings(): void; + getBinder(parameter: string | number): ECSqlBinder; + getColumnCount(): number; + // @internal + getNativeSql(): string; + get isPrepared(): boolean; + // @internal + prepare(db: IModelJsNative.ECDb, ecsql: string, logErrors?: boolean): void; + reset(): void; + // (undocumented) + get sql(): string; + stepForInsert(): ECSqlInsertResult; + // @internal + get stmt(): ECSqlStatement; + // @internal + tryPrepare(db: IModelJsNative.DgnDb | IModelJsNative.ECDb, ecsql: string, logErrors?: boolean): { + status: DbResult; + message: string; + }; +} + // @beta export interface EditableWorkspaceContainer extends WorkspaceContainer { abandonChanges(): void; @@ -3241,6 +3293,7 @@ export abstract class IModelDb extends IModel { performCheckpoint(): void; // @internal prepareSqliteStatement(sql: string, logErrors?: boolean): SqliteStatement; + // @deprecated prepareStatement(sql: string, logErrors?: boolean): ECSqlStatement; // @deprecated query(ecsql: string, params?: QueryBinder, options?: QueryOptions): AsyncIterableIterator; @@ -3282,6 +3335,7 @@ export abstract class IModelDb extends IModel { readonly tiles: IModelDb.Tiles; static tryFindByKey(key: string): IModelDb | undefined; tryGetMetaData(classFullName: string): EntityMetaData | undefined; + // @deprecated tryPrepareStatement(sql: string): ECSqlStatement | undefined; updateEcefLocation(ecef: EcefLocation): void; // @beta @@ -3294,8 +3348,10 @@ export abstract class IModelDb extends IModel { // @internal get watchFilePathName(): LocalFileName; withPreparedSqliteStatement(sql: string, callback: (stmt: SqliteStatement) => T, logErrors?: boolean): T; + // @deprecated withPreparedStatement(ecsql: string, callback: (stmt: ECSqlStatement) => T, logErrors?: boolean): T; withSqliteStatement(sql: string, callback: (stmt: SqliteStatement) => T, logErrors?: boolean): T; + // @deprecated withStatement(ecsql: string, callback: (stmt: ECSqlStatement) => T, logErrors?: boolean): T; // @beta get workspace(): Workspace; diff --git a/common/api/core-common.api.md b/common/api/core-common.api.md index 292da19f8bad..834d478f7b91 100644 --- a/common/api/core-common.api.md +++ b/common/api/core-common.api.md @@ -7384,6 +7384,7 @@ export interface QueryLimit { // @public export interface QueryOptions extends BaseReaderOptions { abbreviateBlobs?: boolean; + // @deprecated convertClassIdsToClassNames?: boolean; includeMetaData?: boolean; limit?: QueryLimit; @@ -7397,6 +7398,7 @@ export class QueryOptionsBuilder { // (undocumented) getOptions(): QueryOptions; setAbbreviateBlobs(val: boolean): this; + // @deprecated setConvertClassIdsToNames(val: boolean): this; // @internal setDelay(val: number): this; diff --git a/common/api/summary/core-backend.exports.csv b/common/api/summary/core-backend.exports.csv index 295ec0334a02..548a68d4e7f8 100644 --- a/common/api/summary/core-backend.exports.csv +++ b/common/api/summary/core-backend.exports.csv @@ -115,14 +115,20 @@ beta;class;DriverBundleElement public;class;ECDb public;enum;ECDbOpenMode public;interface;ECEnumValue +deprecated;interface;ECEnumValue beta;class;ECSchemaXmlContext public;class;ECSqlBinder public;interface;ECSqlColumnInfo +deprecated;interface;ECSqlColumnInfo public;class;ECSqlInsertResult public;interface;ECSqlRowArg public;class;ECSqlStatement +deprecated;class;ECSqlStatement public;class;ECSqlValue +deprecated;class;ECSqlValue public;class;ECSqlValueIterator +deprecated;class;ECSqlValueIterator +public;class;ECSqlWriteStatement beta;interface;EditableWorkspaceContainer beta;interface;EditableWorkspaceDb public;class;ElementAspect diff --git a/common/changes/@itwin/core-backend/deprecate-ecsql-stuff_2025-02-05-16-39.json b/common/changes/@itwin/core-backend/deprecate-ecsql-stuff_2025-02-05-16-39.json new file mode 100644 index 000000000000..bd42545170b2 --- /dev/null +++ b/common/changes/@itwin/core-backend/deprecate-ecsql-stuff_2025-02-05-16-39.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/core-backend", + "comment": "Depreciate ECSqlStatement API", + "type": "none" + } + ], + "packageName": "@itwin/core-backend" +} \ No newline at end of file diff --git a/common/changes/@itwin/core-common/deprecate-ecsql-stuff_2025-02-05-16-39.json b/common/changes/@itwin/core-common/deprecate-ecsql-stuff_2025-02-05-16-39.json new file mode 100644 index 000000000000..8585b1a00d5d --- /dev/null +++ b/common/changes/@itwin/core-common/deprecate-ecsql-stuff_2025-02-05-16-39.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/core-common", + "comment": "Depreciate ECSqlStatement API", + "type": "none" + } + ], + "packageName": "@itwin/core-common" +} \ No newline at end of file diff --git a/common/changes/@itwin/core-quantity/deprecate-ecsql-stuff_2025-02-05-16-39.json b/common/changes/@itwin/core-quantity/deprecate-ecsql-stuff_2025-02-05-16-39.json new file mode 100644 index 000000000000..9fd594fa39c6 --- /dev/null +++ b/common/changes/@itwin/core-quantity/deprecate-ecsql-stuff_2025-02-05-16-39.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/core-quantity", + "comment": "Depreciate ECSqlStatement API", + "type": "none" + } + ], + "packageName": "@itwin/core-quantity" +} \ No newline at end of file diff --git a/common/changes/@itwin/linear-referencing-backend/deprecate-ecsql-stuff_2025-02-05-16-39.json b/common/changes/@itwin/linear-referencing-backend/deprecate-ecsql-stuff_2025-02-05-16-39.json new file mode 100644 index 000000000000..30cfdfa7a99d --- /dev/null +++ b/common/changes/@itwin/linear-referencing-backend/deprecate-ecsql-stuff_2025-02-05-16-39.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/linear-referencing-backend", + "comment": "Depreciate ECSqlStatement API", + "type": "none" + } + ], + "packageName": "@itwin/linear-referencing-backend" +} \ No newline at end of file diff --git a/common/changes/@itwin/physical-material-backend/deprecate-ecsql-stuff_2025-02-05-16-39.json b/common/changes/@itwin/physical-material-backend/deprecate-ecsql-stuff_2025-02-05-16-39.json new file mode 100644 index 000000000000..8d84edea7db9 --- /dev/null +++ b/common/changes/@itwin/physical-material-backend/deprecate-ecsql-stuff_2025-02-05-16-39.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/physical-material-backend", + "comment": "Depreciate ECSqlStatement API", + "type": "none" + } + ], + "packageName": "@itwin/physical-material-backend" +} \ No newline at end of file diff --git a/common/changes/@itwin/presentation-backend/deprecate-ecsql-stuff_2025-02-05-16-39.json b/common/changes/@itwin/presentation-backend/deprecate-ecsql-stuff_2025-02-05-16-39.json new file mode 100644 index 000000000000..10c7cc8355be --- /dev/null +++ b/common/changes/@itwin/presentation-backend/deprecate-ecsql-stuff_2025-02-05-16-39.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/presentation-backend", + "comment": "Depreciate ECSqlStatement API", + "type": "none" + } + ], + "packageName": "@itwin/presentation-backend" +} \ No newline at end of file diff --git a/core/backend/src/ChangeSummaryManager.ts b/core/backend/src/ChangeSummaryManager.ts index 95e344cae7ad..5f18e9a61844 100644 --- a/core/backend/src/ChangeSummaryManager.ts +++ b/core/backend/src/ChangeSummaryManager.ts @@ -12,7 +12,7 @@ import * as path from "path"; import { BackendLoggerCategory } from "./BackendLoggerCategory"; import { BriefcaseManager } from "./BriefcaseManager"; import { ECDb, ECDbOpenMode } from "./ECDb"; -import { ECSqlStatement } from "./ECSqlStatement"; +import { ECSqlInsertResult, ECSqlStatement, ECSqlWriteStatement } from "./ECSqlStatement"; import { BriefcaseDb, IModelDb, TokenArg } from "./IModelDb"; import { IModelHost, KnownLocations } from "./IModelHost"; import { IModelJsFs } from "./IModelJsFs"; @@ -164,13 +164,13 @@ export class ChangeSummaryManager { private static openChangeCacheFile(changesFile: ECDb, changeCacheFilePath: string): void { changesFile.openDb(changeCacheFilePath, ECDbOpenMode.FileUpgrade); - const actualSchemaVersion: { read: number, write: number, minor: number } = changesFile.withPreparedStatement("SELECT VersionMajor read,VersionWrite write,VersionMinor minor FROM meta.ECSchemaDef WHERE Name='IModelChange'", - (stmt: ECSqlStatement) => { - if (stmt.step() !== DbResult.BE_SQLITE_ROW) - throw new IModelError(DbResult.BE_SQLITE_ERROR, "File is not a valid Change Cache file."); + // eslint-disable-next-line @typescript-eslint/no-deprecated + const actualSchemaVersion: { read: number, write: number, minor: number } = changesFile.withPreparedStatement("SELECT VersionMajor read,VersionWrite write,VersionMinor minor FROM meta.ECSchemaDef WHERE Name='IModelChange'", (stmt: ECSqlStatement) => { + if (stmt.step() !== DbResult.BE_SQLITE_ROW) + throw new IModelError(DbResult.BE_SQLITE_ERROR, "File is not a valid Change Cache file."); - return stmt.getRow(); - }); + return stmt.getRow(); + }); if (actualSchemaVersion.read === ChangeSummaryManager._currentIModelChangeSchemaVersion.read && actualSchemaVersion.write === ChangeSummaryManager._currentIModelChangeSchemaVersion.write && @@ -183,19 +183,19 @@ export class ChangeSummaryManager { private static getExtendedSchemaPath(): string { return path.join(KnownLocations.packageAssetsDir, "IModelChange.02.00.00.ecschema.xml"); } private static isSummaryAlreadyExtracted(changesFile: ECDb, changeSetId: GuidString): Id64String | undefined { - return changesFile.withPreparedStatement("SELECT Summary.Id summaryid FROM imodelchange.ChangeSet WHERE WsgId=?", - (stmt: ECSqlStatement) => { - stmt.bindString(1, changeSetId); - if (DbResult.BE_SQLITE_ROW === stmt.step()) - return stmt.getValue(0).getId(); + // eslint-disable-next-line @typescript-eslint/no-deprecated + return changesFile.withPreparedStatement("SELECT Summary.Id summaryid FROM imodelchange.ChangeSet WHERE WsgId=?", (stmt: ECSqlStatement) => { + stmt.bindString(1, changeSetId); + if (DbResult.BE_SQLITE_ROW === stmt.step()) + return stmt.getValue(0).getId(); - return undefined; - }); + return undefined; + }); } private static addExtendedInfos(changesFile: ECDb, changeSummaryId: Id64String, changesetWsgId: GuidString, changesetParentWsgId?: GuidString, description?: string, changesetPushDate?: string, changeSetUserCreated?: GuidString): void { - changesFile.withPreparedStatement("INSERT INTO imodelchange.ChangeSet(Summary.Id,WsgId,ParentWsgId,Description,PushDate,UserCreated) VALUES(?,?,?,?,?,?)", - (stmt: ECSqlStatement) => { + changesFile.withCachedWriteStatement("INSERT INTO imodelchange.ChangeSet(Summary.Id,WsgId,ParentWsgId,Description,PushDate,UserCreated) VALUES(?,?,?,?,?,?)", + (stmt: ECSqlWriteStatement) => { stmt.bindId(1, changeSummaryId); stmt.bindString(2, changesetWsgId); if (changesetParentWsgId) @@ -210,9 +210,9 @@ export class ChangeSummaryManager { if (changeSetUserCreated) stmt.bindString(6, changeSetUserCreated); - const r: DbResult = stmt.step(); - if (r !== DbResult.BE_SQLITE_DONE) - throw new IModelError(r, `Failed to add changeset information to extracted change summary ${changeSummaryId}`); + const r: ECSqlInsertResult = stmt.stepForInsert(); + if (r.status !== DbResult.BE_SQLITE_DONE) + throw new IModelError(r.status, `Failed to add changeset information to extracted change summary ${changeSummaryId}`); }); } @@ -231,15 +231,15 @@ export class ChangeSummaryManager { if (!ChangeSummaryManager.isChangeCacheAttached(iModel)) throw new IModelError(IModelStatus.BadArg, "Change Cache file must be attached to iModel."); - return iModel.withPreparedStatement("SELECT WsgId,ParentWsgId,Description,PushDate,UserCreated FROM ecchange.imodelchange.ChangeSet WHERE Summary.Id=?", - (stmt: ECSqlStatement) => { - stmt.bindId(1, changeSummaryId); - if (stmt.step() !== DbResult.BE_SQLITE_ROW) - throw new IModelError(IModelStatus.BadArg, `No ChangeSet information found for ChangeSummary ${changeSummaryId}.`); + // eslint-disable-next-line @typescript-eslint/no-deprecated + return iModel.withPreparedStatement("SELECT WsgId,ParentWsgId,Description,PushDate,UserCreated FROM ecchange.imodelchange.ChangeSet WHERE Summary.Id=?", (stmt: ECSqlStatement) => { + stmt.bindId(1, changeSummaryId); + if (stmt.step() !== DbResult.BE_SQLITE_ROW) + throw new IModelError(IModelStatus.BadArg, `No ChangeSet information found for ChangeSummary ${changeSummaryId}.`); - const row = stmt.getRow(); - return { id: changeSummaryId, changeSet: { wsgId: row.wsgId, parentWsgId: row.parentWsgId, description: row.description, pushDate: row.pushDate, userCreated: row.userCreated } }; - }); + const row = stmt.getRow(); + return { id: changeSummaryId, changeSet: { wsgId: row.wsgId, parentWsgId: row.parentWsgId, description: row.description, pushDate: row.pushDate, userCreated: row.userCreated } }; + }); } /** Queries the InstanceChange for the specified instance change id. @@ -258,23 +258,26 @@ export class ChangeSummaryManager { throw new IModelError(IModelStatus.BadArg, "Change Cache file must be attached to iModel."); // query instance changes + // eslint-disable-next-line @typescript-eslint/no-deprecated const instanceChange: InstanceChange = iModel.withPreparedStatement(`SELECT ic.Summary.Id summaryId, s.Name changedInstanceSchemaName, c.Name changedInstanceClassName, ic.ChangedInstance.Id changedInstanceId, ic.OpCode, ic.IsIndirect FROM ecchange.change.InstanceChange ic JOIN main.meta.ECClassDef c ON c.ECInstanceId = ic.ChangedInstance.ClassId - JOIN main.meta.ECSchemaDef s ON c.Schema.Id = s.ECInstanceId WHERE ic.ECInstanceId =? `, (stmt: ECSqlStatement) => { - stmt.bindId(1, instanceChangeId); - if (stmt.step() !== DbResult.BE_SQLITE_ROW) - throw new IModelError(IModelStatus.BadArg, `No InstanceChange found for id ${instanceChangeId}.`); + JOIN main.meta.ECSchemaDef s ON c.Schema.Id = s.ECInstanceId WHERE ic.ECInstanceId =? `, + // eslint-disable-next-line @typescript-eslint/no-deprecated + (stmt: ECSqlStatement) => { + stmt.bindId(1, instanceChangeId); + if (stmt.step() !== DbResult.BE_SQLITE_ROW) + throw new IModelError(IModelStatus.BadArg, `No InstanceChange found for id ${instanceChangeId}.`); - const row = stmt.getRow(); - const changedInstanceId: Id64String = row.changedInstanceId; - const changedInstanceClassName: string = `[${row.changedInstanceSchemaName}].[${row.changedInstanceClassName}]`; - const op: ChangeOpCode = row.opCode as ChangeOpCode; - - return { - id: instanceChangeId, summaryId: row.summaryId, changedInstance: { id: changedInstanceId, className: changedInstanceClassName }, - opCode: op, isIndirect: row.isIndirect, - }; - }); + const row = stmt.getRow(); + const changedInstanceId: Id64String = row.changedInstanceId; + const changedInstanceClassName: string = `[${row.changedInstanceSchemaName}].[${row.changedInstanceClassName}]`; + const op: ChangeOpCode = row.opCode as ChangeOpCode; + + return { + id: instanceChangeId, summaryId: row.summaryId, changedInstance: { id: changedInstanceId, className: changedInstanceClassName }, + opCode: op, isIndirect: row.isIndirect, + }; + }); return instanceChange; } @@ -288,31 +291,31 @@ export class ChangeSummaryManager { * @throws [IModelError]($common) if the change cache file hasn't been attached, or in case of other errors. */ public static getChangedPropertyValueNames(iModel: IModelDb, instanceChangeId: Id64String): string[] { - return iModel.withPreparedStatement("SELECT AccessString FROM ecchange.change.PropertyValueChange WHERE InstanceChange.Id=?", - (stmt: ECSqlStatement) => { - stmt.bindId(1, instanceChangeId); + // eslint-disable-next-line @typescript-eslint/no-deprecated + return iModel.withPreparedStatement("SELECT AccessString FROM ecchange.change.PropertyValueChange WHERE InstanceChange.Id=?", (stmt: ECSqlStatement) => { + stmt.bindId(1, instanceChangeId); - const selectClauseItems: string[] = []; - while (stmt.step() === DbResult.BE_SQLITE_ROW) { - // access string tokens need to be escaped as they might collide with reserved words in ECSQL or SQLite - const accessString: string = stmt.getValue(0).getString(); - const accessStringTokens: string[] = accessString.split("."); - assert(accessStringTokens.length > 0); - - let isFirstToken: boolean = true; - let item: string = ""; - for (const token of accessStringTokens) { - if (!isFirstToken) - item += "."; - - item += `[${token}]`; - isFirstToken = false; - } - selectClauseItems.push(item); + const selectClauseItems: string[] = []; + while (stmt.step() === DbResult.BE_SQLITE_ROW) { + // access string tokens need to be escaped as they might collide with reserved words in ECSQL or SQLite + const accessString: string = stmt.getValue(0).getString(); + const accessStringTokens: string[] = accessString.split("."); + assert(accessStringTokens.length > 0); + + let isFirstToken: boolean = true; + let item: string = ""; + for (const token of accessStringTokens) { + if (!isFirstToken) + item += "."; + + item += `[${token}]`; + isFirstToken = false; } + selectClauseItems.push(item); + } - return selectClauseItems; - }); + return selectClauseItems; + }); } /** Builds the ECSQL to query the property value changes for the specified instance change and the specified ChangedValueState. diff --git a/core/backend/src/ChangesetECAdaptor.ts b/core/backend/src/ChangesetECAdaptor.ts index 764efc702a76..bb445d6dfa08 100644 --- a/core/backend/src/ChangesetECAdaptor.ts +++ b/core/backend/src/ChangesetECAdaptor.ts @@ -472,6 +472,7 @@ export class PartialECChangeUnifier { if (rhs.$meta.fallbackClassId && lhs.$meta.fallbackClassId && db && rhs.$meta.fallbackClassId !== lhs.$meta.fallbackClassId) { const lhsClassId = lhs.$meta.fallbackClassId; const rhsClassId = rhs.$meta.fallbackClassId; + // eslint-disable-next-line @typescript-eslint/no-deprecated const isRhsIsSubClassOfLhs = db.withPreparedStatement("SELECT ec_instanceof(?,?)", (stmt) => { stmt.bindId(1, rhsClassId); stmt.bindId(2, lhsClassId); diff --git a/core/backend/src/ECDb.ts b/core/backend/src/ECDb.ts index 9ec524395c44..3ded0c55821f 100644 --- a/core/backend/src/ECDb.ts +++ b/core/backend/src/ECDb.ts @@ -10,7 +10,7 @@ import { IModelJsNative } from "@bentley/imodeljs-native"; import { DbQueryRequest, ECSchemaProps, ECSqlReader, IModelError, QueryBinder, QueryOptions, QueryOptionsBuilder } from "@itwin/core-common"; import { BackendLoggerCategory } from "./BackendLoggerCategory"; import { ConcurrentQuery } from "./ConcurrentQuery"; -import { ECSqlStatement } from "./ECSqlStatement"; +import { ECSqlStatement, ECSqlWriteStatement } from "./ECSqlStatement"; import { IModelNative } from "./internal/NativePlatform"; import { SqliteStatement, StatementCache } from "./SqliteStatement"; import { _nativeDb } from "./internal/Symbols"; @@ -32,6 +32,7 @@ export enum ECDbOpenMode { */ export class ECDb implements Disposable { private _nativeDb?: IModelJsNative.ECDb; + // eslint-disable-next-line @typescript-eslint/no-deprecated private readonly _statementCache = new StatementCache(); private _sqliteStatementCache = new StatementCache(); @@ -151,6 +152,76 @@ export class ECDb implements Disposable { return this[_nativeDb].getSchemaProps(name); } + /** + * Use a prepared ECSQL statement, potentially from the statement cache. If the requested statement doesn't exist + * in the statement cache, a new statement is prepared. After the callback completes, the statement is reset and saved + * in the statement cache so it can be reused in the future. Use this method for ECSQL statements that will be + * reused often and are expensive to prepare. The statement cache holds the most recently used statements, discarding + * the oldest statements as it fills. For statements you don't intend to reuse, instead use [[withStatement]]. + * @param sql The SQLite SQL statement to execute + * @param callback the callback to invoke on the prepared statement + * @param logErrors Determines if error will be logged if statement fail to prepare + * @returns the value returned by `callback`. + * @see [[withWriteStatement]] + * @beta + */ + public withCachedWriteStatement(ecsql: string, callback: (stmt: ECSqlWriteStatement) => T, logErrors = true): T { + // eslint-disable-next-line @typescript-eslint/no-deprecated + const stmt = this._statementCache.findAndRemove(ecsql) ?? this.prepareStatement(ecsql, logErrors); + const release = () => this._statementCache.addOrDispose(stmt); + try { + const val = callback(new ECSqlWriteStatement(stmt)); + if (val instanceof Promise) { + val.then(release, release); + } else { + release(); + } + return val; + } catch (err) { + release(); + throw err; + } + } + + /** + * Prepared and execute a callback on an ECSQL statement. After the callback completes the statement is disposed. + * Use this method for ECSQL statements are either not expected to be reused, or are not expensive to prepare. + * For statements that will be reused often, instead use [[withPreparedStatement]]. + * @param sql The SQLite SQL statement to execute + * @param callback the callback to invoke on the prepared statement + * @param logErrors Determines if error will be logged if statement fail to prepare + * @returns the value returned by `callback`. + * @see [[withCachedWriteStatement]] + * @beta + */ + public withWriteStatement(ecsql: string, callback: (stmt: ECSqlWriteStatement) => T, logErrors = true): T { + const stmt = this.prepareWriteStatement(ecsql, logErrors); + const release = () => { }; + try { + const val = callback(stmt); + if (val instanceof Promise) { + val.then(release, release); + } else { + release(); + } + return val; + } catch (err) { + release(); + throw err; + } + } + + /** Prepare an ECSQL statement. + * @param ecsql The ECSQL statement to prepare + * @param logErrors Determines if error will be logged if statement fail to prepare + * @throws [IModelError]($common) if there is a problem preparing the statement. + * @beta + */ + public prepareWriteStatement(ecsql: string, logErrors = true): ECSqlWriteStatement { + // eslint-disable-next-line @typescript-eslint/no-deprecated + return new ECSqlWriteStatement(this.prepareStatement(ecsql, logErrors)); + } + /** * Use a prepared ECSQL statement, potentially from the statement cache. If the requested statement doesn't exist * in the statement cache, a new statement is prepared. After the callback completes, the statement is reset and saved @@ -163,8 +234,11 @@ export class ECDb implements Disposable { * @returns the value returned by `callback`. * @see [[withStatement]] * @public + * @deprecated in 4.11. Use [[createQueryReader]] for SELECT statements and [[withCachedWriteStatement]] for INSERT/UPDATE/DELETE instead. */ + // eslint-disable-next-line @typescript-eslint/no-deprecated public withPreparedStatement(ecsql: string, callback: (stmt: ECSqlStatement) => T, logErrors = true): T { + // eslint-disable-next-line @typescript-eslint/no-deprecated const stmt = this._statementCache.findAndRemove(ecsql) ?? this.prepareStatement(ecsql, logErrors); const release = () => this._statementCache.addOrDispose(stmt); try { @@ -191,8 +265,11 @@ export class ECDb implements Disposable { * @returns the value returned by `callback`. * @see [[withPreparedStatement]] * @public + * @deprecated in 4.11. Use [[createQueryReader]] for SELECT statements and [[withWriteStatement]] for INSERT/UPDATE/DELETE instead. */ + // eslint-disable-next-line @typescript-eslint/no-deprecated public withStatement(ecsql: string, callback: (stmt: ECSqlStatement) => T, logErrors = true): T { + // eslint-disable-next-line @typescript-eslint/no-deprecated const stmt = this.prepareStatement(ecsql, logErrors); const release = () => stmt[Symbol.dispose](); try { @@ -213,8 +290,11 @@ export class ECDb implements Disposable { * @param ecsql The ECSQL statement to prepare * @param logErrors Determines if error will be logged if statement fail to prepare * @throws [IModelError]($common) if there is a problem preparing the statement. + * @deprecated in 4.11. Use [[prepareWriteStatement]] when preparing an INSERT/UPDATE/DELETE statement or [[createQueryReader]] to execute a SELECT statement. */ + // eslint-disable-next-line @typescript-eslint/no-deprecated public prepareStatement(ecsql: string, logErrors = true): ECSqlStatement { + // eslint-disable-next-line @typescript-eslint/no-deprecated const stmt = new ECSqlStatement(); stmt.prepare(this[_nativeDb], ecsql, logErrors); return stmt; diff --git a/core/backend/src/ECSqlStatement.ts b/core/backend/src/ECSqlStatement.ts index 7f593e9c3d4c..1fbc65bc58cf 100644 --- a/core/backend/src/ECSqlStatement.ts +++ b/core/backend/src/ECSqlStatement.ts @@ -63,6 +63,8 @@ export interface ECSqlRowArg { * - [Executing ECSQL]($docs/learning/backend/ExecutingECSQL) provides more background on ECSQL and an introduction on how to execute ECSQL with the iTwin.js API. * - [Code Examples]($docs/learning/backend/ECSQLCodeExamples) illustrate the use of the iTwin.js API for executing and working with ECSQL * @public + * @deprecated in 4.11. Use [IModelDb.createQueryReader]($backend) or [ECDb.createQueryReader]($backend) to query. + * For ECDb, use [ECDb.withCachedWriteStatement]($backend) or [ECDb.withWriteStatement]($backend) to Insert/Update/Delete. */ export class ECSqlStatement implements IterableIterator, Disposable { private _stmt: IModelJsNative.ECSqlStatement | undefined; @@ -413,12 +415,240 @@ export class ECSqlStatement implements IterableIterator, Disposable { * * See also: [Code Samples]($docs/learning/backend/ECSQLCodeExamples#working-with-the-query-result) */ + // eslint-disable-next-line @typescript-eslint/no-deprecated public getValue(columnIx: number): ECSqlValue { assert(undefined !== this._stmt); + // eslint-disable-next-line @typescript-eslint/no-deprecated return new ECSqlValue(this._stmt.getValue(columnIx)); } } +/** Executes ECSQL INSERT/UPDATE/DELETE statements. + * + * A statement must be prepared before it can be executed, and it must be released when no longer needed. + * See [ECDb.withCachedWriteStatement]($backend) for a convenient and + * reliable way to prepare, execute, and then release a statement. + * + * A statement may contain parameters that must be filled in before use by the **bind** methods. + * + * Once prepared (and parameters are bound, if any), the statement is executed by calling [ECSqlStatement.stepForInsert]($backend). + * + * > Preparing a statement can be time-consuming. The best way to reduce the effect of this overhead is to cache and reuse prepared + * > statements. A cached prepared statement may be used in different places in an app, as long as the statement is general enough. + * > The key to making this strategy work is to phrase a statement in a general way and use placeholders to represent parameters that will vary on each use. + * + * See also + * - [Executing ECSQL]($docs/learning/backend/ExecutingECSQL) provides more background on ECSQL and an introduction on how to execute ECSQL with the iTwin.js API. + * - [Code Examples]($docs/learning/backend/ECSQLCodeExamples) illustrate the use of the iTwin.js API for executing and working with ECSQL + * @public + */ +export class ECSqlWriteStatement { + // eslint-disable-next-line @typescript-eslint/no-deprecated + private _stmt: ECSqlStatement; + + // eslint-disable-next-line @typescript-eslint/no-deprecated + public constructor(stmt?: ECSqlStatement) { + if (stmt) + this._stmt = stmt; + else { + // eslint-disable-next-line @typescript-eslint/no-deprecated + this._stmt = new ECSqlStatement(); + } + } + + public get sql() { return this._stmt.sql; } + + /** Check if this statement has been prepared successfully or not */ + public get isPrepared(): boolean { return this._stmt.isPrepared; } + + /** Get the underlying ECSqlStatement. Needed until we remove ECSqlStatement. + * @param + * @internal + */ + // eslint-disable-next-line @typescript-eslint/no-deprecated + public get stmt(): ECSqlStatement { return this._stmt; } + + /** Prepare this statement prior to first use. + * @param db The ECDb to prepare the statement against + * @param ecsql The ECSQL statement string to prepare + * @param logErrors Determine if errors are logged or not + * @throws [IModelError]($common) if the ECSQL statement cannot be prepared. Normally, prepare fails due to ECSQL syntax errors or references to tables or properties that do not exist. + * The error.message property will provide details. + * @internal + */ + public prepare(db: IModelJsNative.ECDb, ecsql: string, logErrors = true): void { + this._stmt.prepare(db, ecsql, logErrors); + } + + /** Prepare this statement prior to first use. + * @param db The DgnDb or ECDb to prepare the statement against + * @param ecsql The ECSQL statement string to prepare + * @param logErrors Determine if errors are logged or not, its set to false by default for tryPrepare() + * @returns An object with a `status` member equal to [DbResult.BE_SQLITE_OK]($bentley) on success. Upon error, the `message` member will provide details. + * @internal + */ + public tryPrepare(db: IModelJsNative.DgnDb | IModelJsNative.ECDb, ecsql: string, logErrors = false): { status: DbResult, message: string } { + return this.tryPrepare(db, ecsql, logErrors); + } + + /** Reset this statement so that the next call to step will return the first row, if any. */ + public reset(): void { + this._stmt.reset(); + } + + /** Get the Native SQL statement + * @internal + */ + public getNativeSql(): string { + return this._stmt.getNativeSql(); + } + + /** Binds the specified value to the specified ECSQL parameter. + * The section "[iTwin.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" describes the + * iTwin.js types to be used for the different ECSQL parameter types. + * @param parameter Index (1-based) or name of the parameter + */ + public bindValue(parameter: number | string, val: any): void { this.getBinder(parameter).bind(val); } + + /** Binds null to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + */ + public bindNull(parameter: number | string): void { this.getBinder(parameter).bindNull(); } + + /** Binds a BLOB value to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param BLOB value as either a Uint8Array, ArrayBuffer or a Base64 string + */ + public bindBlob(parameter: number | string, blob: string | Uint8Array | ArrayBuffer | SharedArrayBuffer): void { this.getBinder(parameter).bindBlob(blob); } + + /** Binds a boolean value to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param val Boolean value + */ + public bindBoolean(parameter: number | string, val: boolean): void { this.getBinder(parameter).bindBoolean(val); } + + /** Binds a DateTime value to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param isoDateTimeString DateTime value as ISO8601 string + */ + public bindDateTime(parameter: number | string, isoDateTimeString: string): void { this.getBinder(parameter).bindDateTime(isoDateTimeString); } + + /** Binds a double value to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param val Double value + */ + public bindDouble(parameter: number | string, val: number): void { this.getBinder(parameter).bindDouble(val); } + + /** Binds an GUID value to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param val GUID value + */ + public bindGuid(parameter: number | string, val: GuidString): void { this.getBinder(parameter).bindGuid(val); } + + /** Binds an Id value to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param val Id value + */ + public bindId(parameter: number | string, val: Id64String): void { this.getBinder(parameter).bindId(val); } + + /** Binds an integer value to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param val Integer value as number, decimal string or hexadecimal string. + */ + public bindInteger(parameter: number | string, val: number | string): void { this.getBinder(parameter).bindInteger(val); } + + /** Binds an Point2d value to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param val Point2d value + */ + public bindPoint2d(parameter: number | string, val: XAndY): void { this.getBinder(parameter).bindPoint2d(val); } + + /** Binds an Point3d value to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param val Point3d value + */ + public bindPoint3d(parameter: number | string, val: XYAndZ): void { this.getBinder(parameter).bindPoint3d(val); } + + /** Binds a Range3d as a blob to the specified ECSQL parameter + * @param parameter Index(1-based) or name of the parameter + * @param val Range3d value + */ + public bindRange3d(parameter: number | string, val: LowAndHighXYZ): void { this.getBinder(parameter).bindRange3d(val); } + + /** Binds an string to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param val String value + */ + public bindString(parameter: number | string, val: string): void { this.getBinder(parameter).bindString(val); } + + /** Binds a navigation property value to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param val Navigation property value + */ + public bindNavigation(parameter: number | string, val: NavigationBindingValue): void { this.getBinder(parameter).bindNavigation(val); } + + /** Binds a struct property value to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param val Struct value. The struct value is an object composed of pairs of a struct member property name and its value + * (of one of the supported types) + */ + public bindStruct(parameter: number | string, val: object): void { this.getBinder(parameter).bindStruct(val); } + + /** Binds an array value to the specified ECSQL parameter. + * @param parameter Index (1-based) or name of the parameter + * @param val Array value. The array value is an array of values of the supported types + */ + public bindArray(parameter: number | string, val: any[]): void { this.getBinder(parameter).bindArray(val); } + + public bindIdSet(parameter: number | string, val: Id64String[]): void { this.getBinder(parameter).bindIdSet(val); } + /** + * Gets a binder to bind a value for an ECSQL parameter + * > This is the most low-level API to bind a value to a specific parameter. Alternatively you can use the ECSqlStatement.bindXX methods + * > or [ECSqlStatement.bindValues]($backend). + * @param parameter Index (1-based) or name of the parameter + */ + public getBinder(parameter: string | number): ECSqlBinder { + return this._stmt.getBinder(parameter); + } + + /** Bind values to all parameters in the statement. + * @param values The values to bind to the parameters. + * Pass an *array* of values if the parameters are *positional*. + * Pass an *object of the values keyed on the parameter name* for *named parameters*. + * The values in either the array or object must match the respective types of the parameter. + * + * The section "[iTwin.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" describes the + * iTwin.js types to be used for the different ECSQL parameter types. + * + * See also these [Code Samples]($docs/learning/backend/ECSQLCodeExamples#binding-to-all-parameters-at-once) + */ + public bindValues(values: any[] | object): void { + this._stmt.bindValues(values); + } + + /** Clear any bindings that were previously set on this statement. + * @throws [IModelError]($common) in case of errors + */ + public clearBindings(): void { + this._stmt.clearBindings(); + } + + /** Step this INSERT statement and returns status and the ECInstanceId of the newly + * created instance. + * + * > Insert statements can be used with ECDb only, not with IModelDb. + * + * @returns Returns the generated ECInstanceId in case of success and the status of the step + * call. In case of error, the respective error code is returned. + */ + public stepForInsert(): ECSqlInsertResult { + return this._stmt.stepForInsert(); + } + + /** Get the query result's column count (only for ECSQL SELECT statements). */ + public getColumnCount(): number { return this._stmt.getColumnCount(); } +} + /** Binds a value to an ECSQL parameter. * * See also: @@ -601,6 +831,7 @@ export class ECSqlBinder { * - [[ECSqlStatement.getValue]] * - [Code Samples]($docs/learning/backend/ECSQLCodeExamples#working-with-the-query-result) * @public + * @deprecated in 4.11. Use [IModelDb.createQueryReader]($backend) or [ECDb.createQueryReader]($backend) instead. */ export interface ECEnumValue { schema: string; @@ -616,7 +847,8 @@ export interface ECEnumValue { * - [ECSqlStatement.getValue]($backend) * - [Code Samples]($docs/learning/backend/ECSQLCodeExamples#working-with-the-query-result) * @public - */ + * @deprecated in 4.11. Use [IModelDb.createQueryReader]($backend) or [ECDb.createQueryReader]($backend) instead. +*/ export class ECSqlValue { private _val: IModelJsNative.ECSqlValue; @@ -624,6 +856,7 @@ export class ECSqlValue { public constructor(val: IModelJsNative.ECSqlValue) { this._val = val; } /** Get information about the query result's column this value refers to. */ + // eslint-disable-next-line @typescript-eslint/no-deprecated public get columnInfo(): ECSqlColumnInfo { return this._val.getColumnInfo() as ECSqlColumnInfo; } /** Get the value of this ECSQL value */ @@ -670,18 +903,21 @@ export class ECSqlValue { * @return ECEnumeration value(s) or undefined if the ECSqlValue does not represent an ECEnumeration. * or is not a strict match of an ECEnumerator or a combination of them. */ + // eslint-disable-next-line @typescript-eslint/no-deprecated public getEnum(): ECEnumValue[] | undefined { return this._val.getEnum(); } /** Get the value as [NavigationValue]($common) */ public getNavigation(): NavigationValue { return this._val.getNavigation(); } /** Get an iterator for iterating the struct members of this struct value. */ + // eslint-disable-next-line @typescript-eslint/no-deprecated public getStructIterator(): ECSqlValueIterator { return new ECSqlValueIterator(this._val.getStructIterator()); } /** Get this struct value's content as object literal */ public getStruct(): any { return ECSqlValueHelper.getStruct(this); } /** Get an iterator for iterating the array elements of this array value. */ + // eslint-disable-next-line @typescript-eslint/no-deprecated public getArrayIterator(): ECSqlValueIterator { return new ECSqlValueIterator(this._val.getArrayIterator()); } /** Get this array value as JavaScript array */ @@ -691,26 +927,30 @@ export class ECSqlValue { /** Iterator over members of a struct [ECSqlValue]($backend) or the elements of an array [ECSqlValue]($backend). * See [ECSqlValue.getStructIterator]($backend) or [ECSqlValue.getArrayIterator]($backend). * @public - */ + * @deprecated in 4.11. Use [IModelDb.createQueryReader]($backend) or [ECDb.createQueryReader]($backend) instead. +*/ export class ECSqlValueIterator implements IterableIterator { private _it: IModelJsNative.ECSqlValueIterator; /** @internal */ public constructor(it: IModelJsNative.ECSqlValueIterator) { this._it = it; } + // eslint-disable-next-line @typescript-eslint/no-deprecated public next(): IteratorResult { - if (this._it.moveNext()) + if (this._it.moveNext()) { + // eslint-disable-next-line @typescript-eslint/no-deprecated return { done: false, value: new ECSqlValue(this._it.getCurrent()) }; - + } return { done: true, value: undefined }; } - + // eslint-disable-next-line @typescript-eslint/no-deprecated public [Symbol.iterator](): IterableIterator { return this; } } /** Information about an ECSQL column in an ECSQL query result. * See [ECSqlValue.columnInfo]($backend), [ECSqlStatement.getValue]($backend), [ECSqlStatement]($backend) * @public + * @deprecated in 4.11. Use [IModelDb.createQueryReader]($backend) or [ECDb.createQueryReader]($backend) instead. */ export interface ECSqlColumnInfo { /** Gets the data type of the column. @@ -891,6 +1131,7 @@ class ECSqlBindingHelper { } class ECSqlValueHelper { + // eslint-disable-next-line @typescript-eslint/no-deprecated public static getValue(ecsqlValue: ECSqlValue): any { if (ecsqlValue.isNull) return undefined; @@ -912,6 +1153,7 @@ class ECSqlValueHelper { } } + // eslint-disable-next-line @typescript-eslint/no-deprecated public static getStruct(ecsqlValue: ECSqlValue): any { if (ecsqlValue.isNull) return undefined; @@ -933,6 +1175,7 @@ class ECSqlValueHelper { return structVal; } + // eslint-disable-next-line @typescript-eslint/no-deprecated public static getArray(ecsqlValue: ECSqlValue): any[] { const arrayVal: any[] = []; const it = ecsqlValue.getArrayIterator(); @@ -946,10 +1189,12 @@ class ECSqlValueHelper { return arrayVal; } + // eslint-disable-next-line @typescript-eslint/no-deprecated private static getPrimitiveValue(ecsqlValue: ECSqlValue): any { if (ecsqlValue.isNull) return undefined; + // eslint-disable-next-line @typescript-eslint/no-deprecated const colInfo: ECSqlColumnInfo = ecsqlValue.columnInfo; switch (colInfo.getType()) { case ECSqlValueType.Blob: @@ -988,7 +1233,9 @@ class ECSqlValueHelper { if (!tableSpace) tableSpace = "main"; + // eslint-disable-next-line @typescript-eslint/no-deprecated return ecdb.withPreparedStatement(`SELECT s.Name, c.Name FROM [${tableSpace}].meta.ECSchemaDef s, JOIN [${tableSpace}].meta.ECClassDef c ON s.ECInstanceId=c.SchemaId WHERE c.ECInstanceId=?`, + // eslint-disable-next-line @typescript-eslint/no-deprecated (stmt: ECSqlStatement) => { stmt.bindId(1, classId); if (stmt.step() !== DbResult.BE_SQLITE_ROW) diff --git a/core/backend/src/ElementAspect.ts b/core/backend/src/ElementAspect.ts index abbe6244818b..3e0642decb2a 100644 --- a/core/backend/src/ElementAspect.ts +++ b/core/backend/src/ElementAspect.ts @@ -186,6 +186,7 @@ export class ExternalSourceAspect extends ElementMultiAspect { const sql = `SELECT Element.Id, ECInstanceId FROM ${ExternalSourceAspect.classFullName} WHERE (Scope.Id=:scope AND Kind=:kind AND Identifier=:identifier)`; let elementId: Id64String | undefined; let aspectId: Id64String | undefined; + // eslint-disable-next-line @typescript-eslint/no-deprecated iModelDb.withPreparedStatement(sql, (statement: ECSqlStatement) => { statement.bindId("scope", scope); statement.bindString("kind", kind); @@ -213,6 +214,7 @@ export class ExternalSourceAspect extends ElementMultiAspect { public static findAllBySource(iModelDb: IModelDb, scope: Id64String, kind: string, identifier: string): Array<{ elementId: Id64String, aspectId: Id64String }> { const sql = `SELECT Element.Id, ECInstanceId FROM ${ExternalSourceAspect.classFullName} WHERE (Scope.Id=:scope AND Kind=:kind AND Identifier=:identifier)`; const found: Array<{ elementId: Id64String, aspectId: Id64String }> = []; + // eslint-disable-next-line @typescript-eslint/no-deprecated iModelDb.withPreparedStatement(sql, (statement: ECSqlStatement) => { statement.bindId("scope", scope); statement.bindString("kind", kind); diff --git a/core/backend/src/ElementTreeWalker.ts b/core/backend/src/ElementTreeWalker.ts index bc83aec030e6..eeccc7704fde 100644 --- a/core/backend/src/ElementTreeWalker.ts +++ b/core/backend/src/ElementTreeWalker.ts @@ -35,6 +35,7 @@ function sortChildrenBeforeParents(iModel: IModelDb, ids: Id64Array): Array { stmt.bindId(1, modelId); stmt.step(); @@ -151,6 +152,7 @@ function logModel(op: string, iModel: IModelDb, modelId: Id64String, scope?: Ele Logger.logTrace(loggerCategory, `${op} ${fmtModel(model)} ${scope ? scope.toString() : ""}`); if (logElements) { + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement(`select ecinstanceid from ${Element.classFullName} where Model.Id = ?`, (stmt) => { stmt.bindId(1, modelId); while (stmt.step() === DbResult.BE_SQLITE_ROW) { @@ -223,6 +225,7 @@ export abstract class ElementTreeBottomUp { private _processSubModel(model: Model, parenScope: ElementTreeWalkerScope): void { const scope = new ElementTreeWalkerScope(parenScope, model); // Visit only the top-level parents. processElementTree will visit their children (bottom-up). + // eslint-disable-next-line @typescript-eslint/no-deprecated model.iModel.withPreparedStatement(`select ECInstanceId from bis:Element where Model.id=? and Parent.Id is null`, (stmt) => { stmt.bindId(1, model.id); while (stmt.step() === DbResult.BE_SQLITE_ROW) { @@ -386,6 +389,7 @@ abstract class ElementTreeTopDown { private _processSubModel(subModel: Model, scope: ElementTreeWalkerScope) { const subModelScope = new ElementTreeWalkerScope(scope, subModel); // Visit only the top-level parents. processElementTree will recurse into their children. + // eslint-disable-next-line @typescript-eslint/no-deprecated this._iModel.withPreparedStatement(`select ECInstanceId from bis:Element where Model.id=? and Parent.Id is null`, (stmt) => { stmt.bindId(1, subModel.id); while (stmt.step() === DbResult.BE_SQLITE_ROW) { diff --git a/core/backend/src/IModelDb.ts b/core/backend/src/IModelDb.ts index aa38d3bd1836..6a27784fce5a 100644 --- a/core/backend/src/IModelDb.ts +++ b/core/backend/src/IModelDb.ts @@ -224,6 +224,7 @@ export abstract class IModelDb extends IModel { /** @beta */ public readonly channels: ChannelControl = createChannelControl(this); private _relationships?: Relationships; + // eslint-disable-next-line @typescript-eslint/no-deprecated private readonly _statementCache = new StatementCache(); private readonly _sqliteStatementCache = new StatementCache(); private _codeSpecs?: CodeSpecs; @@ -459,8 +460,11 @@ export abstract class IModelDb extends IModel { * @returns the value returned by `callback`. * @see [[withStatement]] * @public + * @deprecated in 4.11. Use [[createQueryReader]] instead. */ + // eslint-disable-next-line @typescript-eslint/no-deprecated public withPreparedStatement(ecsql: string, callback: (stmt: ECSqlStatement) => T, logErrors = true): T { + // eslint-disable-next-line @typescript-eslint/no-deprecated const stmt = this._statementCache.findAndRemove(ecsql) ?? this.prepareStatement(ecsql, logErrors); const release = () => this._statementCache.addOrDispose(stmt); try { @@ -487,8 +491,11 @@ export abstract class IModelDb extends IModel { * @returns the value returned by `callback`. * @see [[withPreparedStatement]] * @public + * @deprecated in 4.11. Use [[createQueryReader]] instead. */ + // eslint-disable-next-line @typescript-eslint/no-deprecated public withStatement(ecsql: string, callback: (stmt: ECSqlStatement) => T, logErrors = true): T { + // eslint-disable-next-line @typescript-eslint/no-deprecated const stmt = this.prepareStatement(ecsql, logErrors); const release = () => stmt[Symbol.dispose](); try { @@ -737,6 +744,7 @@ export abstract class IModelDb extends IModel { sql += ` OFFSET ${params.offset}`; const ids = new Set(); + // eslint-disable-next-line @typescript-eslint/no-deprecated this.withPreparedStatement(sql, (stmt) => { if (params.bindings) stmt.bindValues(params.bindings); @@ -1089,8 +1097,11 @@ export abstract class IModelDb extends IModel { * @param sql The ECSQL statement to prepare * @param logErrors Determines if error will be logged if statement fail to prepare * @throws [[IModelError]] if there is a problem preparing the statement. + * @deprecated in 4.11. Use [IModelDb.createQueryReader]($backend) or [ECDb.createQueryReader]($backend) to query. */ + // eslint-disable-next-line @typescript-eslint/no-deprecated public prepareStatement(sql: string, logErrors = true): ECSqlStatement { + // eslint-disable-next-line @typescript-eslint/no-deprecated const stmt = new ECSqlStatement(); stmt.prepare(this[_nativeDb], sql, logErrors); return stmt; @@ -1099,8 +1110,11 @@ export abstract class IModelDb extends IModel { /** Prepare an ECSQL statement. * @param sql The ECSQL statement to prepare * @returns `undefined` if there is a problem preparing the statement. + * @deprecated in 4.11. Use [IModelDb.createQueryReader]($backend) or [ECDb.createQueryReader]($backend) to query. */ + // eslint-disable-next-line @typescript-eslint/no-deprecated public tryPrepareStatement(sql: string): ECSqlStatement | undefined { + // eslint-disable-next-line @typescript-eslint/no-deprecated const statement = new ECSqlStatement(); const result = statement.tryPrepare(this[_nativeDb], sql); return DbResult.BE_SQLITE_OK === result.status ? statement : undefined; @@ -1229,6 +1243,7 @@ export abstract class IModelDb extends IModel { */ public querySchemaVersion(schemaName: string): string | undefined { const sql = `SELECT VersionMajor,VersionWrite,VersionMinor FROM ECDbMeta.ECSchemaDef WHERE Name=:schemaName LIMIT 1`; + // eslint-disable-next-line @typescript-eslint/no-deprecated return this.withPreparedStatement(sql, (statement: ECSqlStatement): string | undefined => { statement.bindString("schemaName", schemaName); if (DbResult.BE_SQLITE_ROW === statement.step()) { @@ -1613,6 +1628,7 @@ export namespace IModelDb { */ public queryLastModifiedTime(modelId: Id64String): string { const sql = `SELECT LastMod FROM ${Model.classFullName} WHERE ECInstanceId=:modelId`; + // eslint-disable-next-line @typescript-eslint/no-deprecated return this._iModel.withPreparedStatement(sql, (statement) => { statement.bindId("modelId", modelId); if (DbResult.BE_SQLITE_ROW === statement.step()) { @@ -1948,7 +1964,7 @@ export namespace IModelDb { if (code.value === undefined) throw new IModelError(IModelStatus.InvalidCode, "Invalid Code"); - + // eslint-disable-next-line @typescript-eslint/no-deprecated return this._iModel.withPreparedStatement("SELECT ECInstanceId FROM BisCore:Element WHERE CodeSpec.Id=? AND CodeScope.Id=? AND CodeValue=?", (stmt: ECSqlStatement) => { stmt.bindId(1, code.spec); stmt.bindId(2, Id64.fromString(code.scope)); @@ -1966,6 +1982,7 @@ export namespace IModelDb { */ public queryLastModifiedTime(elementId: Id64String): string { const sql = "SELECT LastMod FROM BisCore:Element WHERE ECInstanceId=:elementId"; + // eslint-disable-next-line @typescript-eslint/no-deprecated return this._iModel.withPreparedStatement(sql, (statement: ECSqlStatement): string => { statement.bindId("elementId", elementId); if (DbResult.BE_SQLITE_ROW === statement.step()) @@ -2126,6 +2143,7 @@ export namespace IModelDb { */ public queryChildren(elementId: Id64String): Id64String[] { const sql = "SELECT ECInstanceId FROM BisCore:Element WHERE Parent.Id=:elementId"; + // eslint-disable-next-line @typescript-eslint/no-deprecated return this._iModel.withPreparedStatement(sql, (statement: ECSqlStatement): Id64String[] => { statement.bindId("elementId", elementId); const childIds: Id64String[] = []; @@ -2142,6 +2160,7 @@ export namespace IModelDb { * @throws [[IModelError]] if the element does not exist */ public queryParent(elementId: Id64String): Id64String | undefined { + // eslint-disable-next-line @typescript-eslint/no-deprecated return this._iModel.withPreparedStatement(`select parent.id from ${Element.classFullName} where ecinstanceid=?`, (stmt) => { stmt.bindId(1, elementId); if (stmt.step() !== DbResult.BE_SQLITE_ROW) @@ -2160,6 +2179,7 @@ export namespace IModelDb { // A sub-model will have the same Id value as the element it is describing const sql = "SELECT ECInstanceId FROM BisCore:Model WHERE ECInstanceId=:elementId"; + // eslint-disable-next-line @typescript-eslint/no-deprecated return this._iModel.withPreparedStatement(sql, (statement: ECSqlStatement): boolean => { statement.bindId("elementId", elementId); return DbResult.BE_SQLITE_ROW === statement.step(); @@ -2176,6 +2196,7 @@ export namespace IModelDb { */ public _queryAspects(elementId: Id64String, fromClassFullName: string, excludedClassFullNames?: Set): ElementAspect[] { // eslint-disable-line @typescript-eslint/naming-convention const sql = `SELECT ECInstanceId,ECClassId FROM ${fromClassFullName} WHERE Element.Id=:elementId ORDER BY ECClassId,ECInstanceId`; // ORDER BY to maximize statement reuse + // eslint-disable-next-line @typescript-eslint/no-deprecated return this._iModel.withPreparedStatement(sql, (statement: ECSqlStatement): ElementAspect[] => { statement.bindId("elementId", elementId); const aspects: ElementAspect[] = []; @@ -2195,6 +2216,7 @@ export namespace IModelDb { */ private _queryAspect(aspectInstanceId: Id64String, aspectClassName: string): ElementAspect { const sql = `SELECT * FROM ${aspectClassName} WHERE ECInstanceId=:aspectInstanceId`; + // eslint-disable-next-line @typescript-eslint/no-deprecated const aspect: ElementAspectProps | undefined = this._iModel.withPreparedStatement(sql, (statement: ECSqlStatement): ElementAspectProps | undefined => { statement.bindId("aspectInstanceId", aspectInstanceId); if (DbResult.BE_SQLITE_ROW === statement.step()) { @@ -2216,6 +2238,7 @@ export namespace IModelDb { */ public getAspect(aspectInstanceId: Id64String): ElementAspect { const sql = "SELECT ECClassId FROM BisCore:ElementAspect WHERE ECInstanceId=:aspectInstanceId"; + // eslint-disable-next-line @typescript-eslint/no-deprecated const aspectClassFullName = this._iModel.withPreparedStatement(sql, (statement: ECSqlStatement): string | undefined => { statement.bindId("aspectInstanceId", aspectInstanceId); return (DbResult.BE_SQLITE_ROW === statement.step()) ? statement.getValue(0).getClassNameForClassId().replace(".", ":") : undefined; @@ -2229,6 +2252,7 @@ export namespace IModelDb { private static classMap = new Map(); private runInstanceQuery(sql: string, elementId: Id64String, excludedClassFullNames?: Set): ElementAspect[] { + // eslint-disable-next-line @typescript-eslint/no-deprecated return this._iModel.withPreparedStatement(sql, (statement: ECSqlStatement) => { statement.bindId("elementId", elementId); const aspects: ElementAspect[] = []; @@ -2279,11 +2303,14 @@ export namespace IModelDb { let classIdList = IModelDb.Elements.classMap.get(aspectClassFullName); if (classIdList === undefined) { const classIds: string[] = []; + // eslint-disable-next-line @typescript-eslint/no-deprecated this._iModel.withPreparedStatement(`select SourceECInstanceId from meta.ClassHasAllBaseClasses where TargetECInstanceId = (select ECInstanceId from meta.ECClassDef where Name='${fullClassName[1]}' - and Schema.Id = (select ECInstanceId from meta.ECSchemaDef where Name='${fullClassName[0]}')) and SourceECInstanceId != TargetECInstanceId`, (statement: ECSqlStatement) => { - while (statement.step() === DbResult.BE_SQLITE_ROW) - classIds.push(statement.getValue(0).getId()); - }); + and Schema.Id = (select ECInstanceId from meta.ECSchemaDef where Name='${fullClassName[0]}')) and SourceECInstanceId != TargetECInstanceId`, + // eslint-disable-next-line @typescript-eslint/no-deprecated + (statement: ECSqlStatement) => { + while (statement.step() === DbResult.BE_SQLITE_ROW) + classIds.push(statement.getValue(0).getId()); + }); if (classIds.length > 0) { classIdList = classIds.join(","); IModelDb.Elements.classMap.set(aspectClassFullName, classIdList); diff --git a/core/backend/src/Relationship.ts b/core/backend/src/Relationship.ts index 6b22dcdfe05f..689c591fa282 100644 --- a/core/backend/src/Relationship.ts +++ b/core/backend/src/Relationship.ts @@ -487,11 +487,13 @@ export class Relationships { public tryGetInstanceProps(relClassFullName: string, criteria: Id64String | SourceAndTarget): T | undefined { let props: T | undefined; if (typeof criteria === "string") { + // eslint-disable-next-line @typescript-eslint/no-deprecated props = this._iModel.withPreparedStatement(`SELECT * FROM ${relClassFullName} WHERE ecinstanceid=?`, (stmt: ECSqlStatement) => { stmt.bindId(1, criteria); return DbResult.BE_SQLITE_ROW === stmt.step() ? stmt.getRow() as T : undefined; }); } else { + // eslint-disable-next-line @typescript-eslint/no-deprecated props = this._iModel.withPreparedStatement(`SELECT * FROM ${relClassFullName} WHERE SourceECInstanceId=? AND TargetECInstanceId=?`, (stmt: ECSqlStatement) => { stmt.bindId(1, criteria.sourceId); stmt.bindId(2, criteria.targetId); diff --git a/core/backend/src/ViewStore.ts b/core/backend/src/ViewStore.ts index 152826a5c5b5..40ff2f074a1e 100644 --- a/core/backend/src/ViewStore.ts +++ b/core/backend/src/ViewStore.ts @@ -21,6 +21,7 @@ import { Model } from "./Model"; import { Entity } from "./Entity"; import { BlobContainer } from "./BlobContainerService"; import { _nativeDb } from "./internal/Symbols"; +import { ECSqlStatement } from "./ECSqlStatement"; /* eslint-disable @typescript-eslint/no-non-null-assertion */ @@ -866,7 +867,8 @@ export namespace ViewStore { const ids = new Set(); try { - this.iModel.withStatement(sql, (stmt) => { + // eslint-disable-next-line @typescript-eslint/no-deprecated + this.iModel.withPreparedStatement(sql, (stmt: ECSqlStatement) => { if (bindings) stmt.bindValues(bindings); for (const el of stmt) { diff --git a/core/backend/src/internal/ChannelAdmin.ts b/core/backend/src/internal/ChannelAdmin.ts index 79791b6c45b7..bbfbac50c86d 100644 --- a/core/backend/src/internal/ChannelAdmin.ts +++ b/core/backend/src/internal/ChannelAdmin.ts @@ -43,6 +43,7 @@ class ChannelAdmin implements ChannelControl { return ChannelControl.sharedChannelName; try { + // eslint-disable-next-line @typescript-eslint/no-deprecated const channel = this._iModel.withPreparedStatement(`SELECT Owner FROM ${ChannelAdmin.channelClassName} WHERE Element.Id=?`, (stmt) => { stmt.bindId(1, elementId); return DbResult.BE_SQLITE_ROW === stmt.step() ? stmt.getValue(0).getString() : undefined; @@ -114,6 +115,7 @@ class ChannelAdmin implements ChannelControl { return IModel.rootSubjectId; try { + // eslint-disable-next-line @typescript-eslint/no-deprecated const channelRoot = this._iModel.withPreparedStatement(`SELECT Element.Id FROM ${ChannelAdmin.channelClassName} WHERE Owner=?`, (stmt) => { stmt.bindString(1, channelKey); return DbResult.BE_SQLITE_ROW === stmt.step() ? stmt.getValue(0).getId() : undefined; diff --git a/core/backend/src/rpc-impl/IModelReadRpcImpl.ts b/core/backend/src/rpc-impl/IModelReadRpcImpl.ts index 8591c8c39356..d657e04e44c2 100644 --- a/core/backend/src/rpc-impl/IModelReadRpcImpl.ts +++ b/core/backend/src/rpc-impl/IModelReadRpcImpl.ts @@ -246,6 +246,7 @@ export class IModelReadRpcImpl extends RpcInterface implements IModelReadRpcInte public async getAllCodeSpecs(tokenProps: IModelRpcProps): Promise { const codeSpecs: any[] = []; const iModelDb = await getIModelForRpc(tokenProps); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModelDb.withPreparedStatement("SELECT ECInstanceId AS id, name, jsonProperties FROM BisCore.CodeSpec", (statement) => { for (const row of statement) codeSpecs.push({ id: row.id, name: row.name, jsonProperties: JSON.parse(row.jsonProperties) }); diff --git a/core/backend/src/test/IModelTestUtils.ts b/core/backend/src/test/IModelTestUtils.ts index 01c4fcad5386..ecdf05c6665b 100644 --- a/core/backend/src/test/IModelTestUtils.ts +++ b/core/backend/src/test/IModelTestUtils.ts @@ -478,6 +478,7 @@ export class IModelTestUtils { } public static executeQuery(db: IModelDb, ecsql: string, bindings?: any[] | object): any[] { + // eslint-disable-next-line @typescript-eslint/no-deprecated return db.withPreparedStatement(ecsql, (stmt) => { if (bindings) stmt.bindValues(bindings); @@ -618,6 +619,7 @@ export class IModelTestUtils { } public static queryByUserLabel(iModelDb: IModelDb, userLabel: string): Id64String { + // eslint-disable-next-line @typescript-eslint/no-deprecated return iModelDb.withPreparedStatement(`SELECT ECInstanceId FROM ${Element.classFullName} WHERE UserLabel=:userLabel`, (statement: ECSqlStatement): Id64String => { statement.bindString("userLabel", userLabel); return DbResult.BE_SQLITE_ROW === statement.step() ? statement.getValue(0).getId() : Id64.invalid; @@ -625,6 +627,7 @@ export class IModelTestUtils { } public static queryByCodeValue(iModelDb: IModelDb, codeValue: string): Id64String { + // eslint-disable-next-line @typescript-eslint/no-deprecated return iModelDb.withPreparedStatement(`SELECT ECInstanceId FROM ${Element.classFullName} WHERE CodeValue=:codeValue`, (statement: ECSqlStatement): Id64String => { statement.bindString("codeValue", codeValue); return DbResult.BE_SQLITE_ROW === statement.step() ? statement.getValue(0).getId() : Id64.invalid; @@ -662,6 +665,7 @@ export class IModelTestUtils { } IModelJsFs.appendFileSync(outputFileName, `${iModelDb.pathName}\n`); IModelJsFs.appendFileSync(outputFileName, "\n=== CodeSpecs ===\n"); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModelDb.withPreparedStatement(`SELECT ECInstanceId,Name FROM BisCore:CodeSpec ORDER BY ECInstanceId`, (statement: ECSqlStatement): void => { while (DbResult.BE_SQLITE_ROW === statement.step()) { const codeSpecId = statement.getValue(0).getId(); @@ -670,6 +674,7 @@ export class IModelTestUtils { } }); IModelJsFs.appendFileSync(outputFileName, "\n=== Schemas ===\n"); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModelDb.withPreparedStatement(`SELECT Name FROM ECDbMeta.ECSchemaDef ORDER BY ECInstanceId`, (statement: ECSqlStatement): void => { while (DbResult.BE_SQLITE_ROW === statement.step()) { const schemaName: string = statement.getValue(0).getString(); @@ -677,6 +682,7 @@ export class IModelTestUtils { } }); IModelJsFs.appendFileSync(outputFileName, "\n=== Models ===\n"); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModelDb.withPreparedStatement(`SELECT ECInstanceId FROM ${Model.classFullName} ORDER BY ECInstanceId`, (statement: ECSqlStatement): void => { while (DbResult.BE_SQLITE_ROW === statement.step()) { const modelId = statement.getValue(0).getId(); @@ -685,6 +691,7 @@ export class IModelTestUtils { } }); IModelJsFs.appendFileSync(outputFileName, "\n=== ViewDefinitions ===\n"); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModelDb.withPreparedStatement(`SELECT ECInstanceId FROM ${ViewDefinition.classFullName} ORDER BY ECInstanceId`, (statement: ECSqlStatement): void => { while (DbResult.BE_SQLITE_ROW === statement.step()) { const viewDefinitionId = statement.getValue(0).getId(); @@ -693,18 +700,21 @@ export class IModelTestUtils { } }); IModelJsFs.appendFileSync(outputFileName, "\n=== Elements ===\n"); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModelDb.withPreparedStatement(`SELECT COUNT(*) FROM ${Element.classFullName}`, (statement: ECSqlStatement): void => { if (DbResult.BE_SQLITE_ROW === statement.step()) { const count: number = statement.getValue(0).getInteger(); IModelJsFs.appendFileSync(outputFileName, `Count of ${Element.classFullName}=${count}\n`); } }); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModelDb.withPreparedStatement(`SELECT COUNT(*) FROM ${PhysicalObject.classFullName}`, (statement: ECSqlStatement): void => { if (DbResult.BE_SQLITE_ROW === statement.step()) { const count: number = statement.getValue(0).getInteger(); IModelJsFs.appendFileSync(outputFileName, `Count of ${PhysicalObject.classFullName}=${count}\n`); } }); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModelDb.withPreparedStatement(`SELECT COUNT(*) FROM ${GeometryPart.classFullName}`, (statement: ECSqlStatement): void => { if (DbResult.BE_SQLITE_ROW === statement.step()) { const count: number = statement.getValue(0).getInteger(); diff --git a/core/backend/src/test/PrintElementTree.ts b/core/backend/src/test/PrintElementTree.ts index 1d73d7c2dfe2..b2445b10cc82 100644 --- a/core/backend/src/test/PrintElementTree.ts +++ b/core/backend/src/test/PrintElementTree.ts @@ -33,7 +33,7 @@ export function printElementTree(loggerCategory: string, seen: Set, const subModel = iModel.models.tryGetModel(elementId); if (subModel !== undefined) { Logger.logTrace(loggerCategory, `${"\t".repeat(indent)} subModel ${fmtModel(subModel)}:`); - + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement(`select ecinstanceid from ${Element.classFullName} where Model.Id = ?`, (stmt) => { stmt.bindId(1, subModel.id); while (stmt.step() === DbResult.BE_SQLITE_ROW) { diff --git a/core/backend/src/test/SchemaUtils.test.ts b/core/backend/src/test/SchemaUtils.test.ts index 1f8129647e5c..3680058af9c8 100644 --- a/core/backend/src/test/SchemaUtils.test.ts +++ b/core/backend/src/test/SchemaUtils.test.ts @@ -63,6 +63,7 @@ describe("convertEC2Schemas", () => { assert.isTrue(db.isOpen); const propNamesInEntityClass = []; + // eslint-disable-next-line @typescript-eslint/no-deprecated let stmt: ECSqlStatement = db.prepareStatement("SELECT p.Name FROM meta.ECPropertyDef p JOIN meta.ECClassDef c USING meta.ClassOwnsLocalProperties JOIN meta.ECSchemaDef s USING meta.SchemaOwnsClasses WHERE s.Name='TestSchema' AND c.Name='TestEntityClass' ORDER BY p.Ordinal"); let rowCount = 0; while (stmt.step() === DbResult.BE_SQLITE_ROW) { @@ -87,6 +88,7 @@ describe("convertEC2Schemas", () => { assert.isTrue(propNamesInEntityClass.includes("TargetECClassId")); // The TargetECClassId property is allowed on Entity classes and should not be renamed const propNamesInStructClass = []; + // eslint-disable-next-line @typescript-eslint/no-deprecated stmt = db.prepareStatement("SELECT p.Name FROM meta.ECPropertyDef p JOIN meta.ECClassDef c USING meta.ClassOwnsLocalProperties JOIN meta.ECSchemaDef s USING meta.SchemaOwnsClasses WHERE s.Name='TestSchema' AND c.Name='TestStructClass' ORDER BY p.Ordinal"); rowCount = 0; while (stmt.step() === DbResult.BE_SQLITE_ROW) { diff --git a/core/backend/src/test/ecdb/ECDb.test.ts b/core/backend/src/test/ecdb/ECDb.test.ts index e937db906a5f..7ba64cc03be1 100644 --- a/core/backend/src/test/ecdb/ECDb.test.ts +++ b/core/backend/src/test/ecdb/ECDb.test.ts @@ -6,7 +6,7 @@ import { assert, expect } from "chai"; import * as path from "path"; import * as sinon from "sinon"; import { DbResult, Id64, Id64String, Logger } from "@itwin/core-bentley"; -import { ECDb, ECDbOpenMode, ECSqlInsertResult, ECSqlStatement, IModelJsFs, SqliteStatement, SqliteValue, SqliteValueType } from "../../core-backend"; +import { ECDb, ECDbOpenMode, ECSqlInsertResult, ECSqlStatement, ECSqlWriteStatement, IModelJsFs, SqliteStatement, SqliteValue, SqliteValueType } from "../../core-backend"; import { KnownTestLocations } from "../KnownTestLocations"; import { ECDbTestHelper } from "./ECDbTestHelper"; @@ -73,7 +73,7 @@ describe("ECDb", () => { `); assert.isTrue(testECDb.isOpen); - id = testECDb.withPreparedStatement("INSERT INTO test.Person(Name,Age) VALUES('Mary', 45)", (stmt: ECSqlStatement) => { + id = testECDb.withCachedWriteStatement("INSERT INTO test.Person(Name,Age) VALUES('Mary', 45)", (stmt: ECSqlWriteStatement) => { const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); assert.isDefined(res.id); @@ -87,6 +87,7 @@ describe("ECDb", () => { ecdb.openDb(ecdbPath, ECDbOpenMode.Readonly); assert.isTrue(ecdb.isOpen); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Name, Age FROM test.Person WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -203,6 +204,7 @@ describe("ECDb", () => { const expectedLabels = [undefined, "m", "", "mm"]; let index = 0; + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withStatement("select label from meta.FormatCompositeUnitDef where Format.Id=0x1", (stmt: ECSqlStatement) => { for (let i: number = 1; i <= 4; i++) { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -231,6 +233,7 @@ describe("ECDb", () => { const expectedLabelsUpdated = ["", "m", undefined, "mm"]; index = 0; + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withStatement("select label from meta.FormatCompositeUnitDef where Format.Id=0x1", (stmt: ECSqlStatement) => { for (let i: number = 1; i <= 4; i++) { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); diff --git a/core/backend/src/test/ecdb/ECSqlAst.test.ts b/core/backend/src/test/ecdb/ECSqlAst.test.ts index 20fb152b0833..17f747942eff 100644 --- a/core/backend/src/test/ecdb/ECSqlAst.test.ts +++ b/core/backend/src/test/ecdb/ECSqlAst.test.ts @@ -70,6 +70,7 @@ describe("ECSql Abstract Syntax Tree", () => { async function parseECSql(ecsql: string) { const parseTreeECSql = `PRAGMA PARSE_TREE("${ecsql}") ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES`; if (true) { + // eslint-disable-next-line @typescript-eslint/no-deprecated return ecdb.withPreparedStatement(parseTreeECSql, (stmt) => { if (DbResult.BE_SQLITE_ROW !== stmt.step()) { throw new Error("unable to get parse tree."); diff --git a/core/backend/src/test/ecdb/ECSqlQuery.test.ts b/core/backend/src/test/ecdb/ECSqlQuery.test.ts index af425c5aef81..82cebb50de19 100644 --- a/core/backend/src/test/ecdb/ECSqlQuery.test.ts +++ b/core/backend/src/test/ecdb/ECSqlQuery.test.ts @@ -94,6 +94,7 @@ describe("ECSql Query", () => { builder.setRowFormat(QueryRowFormat.UseJsPropertyNames); let expectedRows = 0; for (let i = 0; i < queries.length; i++) { + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel1.withPreparedStatement(queries[i], (stmt: ECSqlStatement) => { assert.equal(DbResult.BE_SQLITE_ROW, stmt.step(), "expected DbResult.BE_SQLITE_ROW"); assert.deepEqual(stmt.getRow(), results[i], `(ECSqlStatement) "${queries[i]}" does not match expected result (${path.basename(imodel1[_nativeDb].getFilePath())})`); @@ -364,9 +365,11 @@ describe("ECSql Query", () => { /* eslint-enable @typescript-eslint/naming-convention */ const builder = new QueryOptionsBuilder(); builder.setRowFormat(QueryRowFormat.UseJsPropertyNames); + // eslint-disable-next-line @typescript-eslint/no-deprecated builder.setConvertClassIdsToNames(true); // With ECDb Profile 4002 for (const testQuery of testQueries) { + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel1.withPreparedStatement(testQuery.query, (stmt: ECSqlStatement) => { assert.equal(DbResult.BE_SQLITE_ROW, stmt.step(), "expected DbResult.BE_SQLITE_ROW"); assert.deepEqual(stmt.getRow(), testQuery.result, `(ECSqlStatement) "${testQuery.query}" does not match expected result (${path.basename(imodel1[_nativeDb].getFilePath())})`); @@ -381,6 +384,7 @@ describe("ECSql Query", () => { } // With ECDb Profile 4003 for (const testQuery of testQueries) { + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel6.withPreparedStatement(testQuery.query, (stmt: ECSqlStatement) => { assert.equal(DbResult.BE_SQLITE_ROW, stmt.step(), "expected DbResult.BE_SQLITE_ROW"); assert.deepEqual(stmt.getRow(), testQuery.result, `(ECSqlStatement) "${testQuery.query}" does not match expected result (${path.basename(imodel1[_nativeDb].getFilePath())})`); @@ -484,12 +488,14 @@ describe("ECSql Query", () => { // expect log message when statement fails slm = new SequentialLogMatcher(); slm.append().error().category("ECDb").message("ECClass 'abc.def' does not exist or could not be loaded."); + // eslint-disable-next-line @typescript-eslint/no-deprecated assert.throw(() => ecdb.withPreparedStatement("SELECT abc FROM abc.def", () => { }), "ECClass 'abc.def' does not exist or could not be loaded."); assert.isTrue(slm.finishAndDispose(), "logMatcher should detect log"); // now pass suppress log error which mean we should not get the error slm = new SequentialLogMatcher(); slm.append().error().category("ECDb").message("ECClass 'abc.def' does not exist or could not be loaded."); + // eslint-disable-next-line @typescript-eslint/no-deprecated assert.throw(() => ecdb.withPreparedStatement("SELECT abc FROM abc.def", () => { }, /* logErrors = */ false), ""); assert.isFalse(slm.finishAndDispose(), "logMatcher should not detect log"); }); @@ -643,7 +649,7 @@ describe("ECSql Query", () => { 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 ids: string[] = ["0x1", "ABC", "YZ"]; const reader = imodel1.createQueryReader("SELECT * FROM BisCore.element, IdSet(?) WHERE id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES", QueryBinder.from([ids])); let props = await reader.getMetaData(); @@ -659,11 +665,11 @@ describe("ECSql Query", () => { 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" + const ids: string[] = ["ABC", "0x1", "YZ"]; // as first value is not an Id so QueryBinder.from will throw error of "unsupported type" - try{ + try { imodel1.createQueryReader("SELECT * FROM BisCore.element, IdSet(?) WHERE id = ECInstanceId ECSQLOPTIONS ENABLE_EXPERIMENTAL_FEATURES", QueryBinder.from([ids])); - }catch(err: any){ + } catch (err: any) { assert.equal(err.message, "unsupported type"); } }); diff --git a/core/backend/src/test/ecdb/ECSqlReader.test.ts b/core/backend/src/test/ecdb/ECSqlReader.test.ts index 18be81c5201c..5e41a6ac1468 100644 --- a/core/backend/src/test/ecdb/ECSqlReader.test.ts +++ b/core/backend/src/test/ecdb/ECSqlReader.test.ts @@ -6,7 +6,7 @@ import { assert } from "chai"; import { DbResult } from "@itwin/core-bentley"; import { ECSqlReader, QueryBinder, QueryOptionsBuilder, QueryRowFormat } from "@itwin/core-common"; import { SnapshotDb } from "../../core-backend"; -import { ECSqlStatement } from "../../ECSqlStatement"; +import { ECSqlWriteStatement } from "../../ECSqlStatement"; import { IModelTestUtils } from "../IModelTestUtils"; import { KnownTestLocations } from "../KnownTestLocations"; import { ECDbTestHelper } from "./ECDbTestHelper"; @@ -71,15 +71,15 @@ describe("ECSqlReader", (() => { `) - 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); + 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 () => { @@ -91,7 +91,7 @@ describe("ECSqlReader", (() => { `); assert.isTrue(ecdb.isOpen); - const r = await ecdb.withStatement("INSERT INTO ts.Foo(n) VALUES(20)", async (stmt: ECSqlStatement) => { + const r = await ecdb.withCachedWriteStatement("INSERT INTO ts.Foo(n) VALUES(20)", async (stmt: ECSqlWriteStatement) => { return stmt.stepForInsert(); }); ecdb.saveChanges(); diff --git a/core/backend/src/test/ecdb/ECSqlStatement.test.ts b/core/backend/src/test/ecdb/ECSqlStatement.test.ts index 9d1e9519bcf3..d1b9f29634b7 100644 --- a/core/backend/src/test/ecdb/ECSqlStatement.test.ts +++ b/core/backend/src/test/ecdb/ECSqlStatement.test.ts @@ -6,7 +6,7 @@ import { assert } from "chai"; import { DbResult, Guid, GuidString, Id64, Id64String } from "@itwin/core-bentley"; import { NavigationValue, QueryBinder, QueryOptions, QueryOptionsBuilder, QueryRowFormat } from "@itwin/core-common"; import { Point2d, Point3d, Range3d, XAndY, XYAndZ } from "@itwin/core-geometry"; -import { _nativeDb, ECDb, ECEnumValue, ECSqlColumnInfo, ECSqlInsertResult, ECSqlStatement, ECSqlValue, SnapshotDb } from "../../core-backend"; +import { _nativeDb, ECDb, ECEnumValue, ECSqlColumnInfo, ECSqlInsertResult, ECSqlStatement, ECSqlValue, ECSqlWriteStatement, SnapshotDb } from "../../core-backend"; import { IModelTestUtils } from "../IModelTestUtils"; import { KnownTestLocations } from "../KnownTestLocations"; import { SequentialLogMatcher } from "../SequentialLogMatcher"; @@ -68,7 +68,7 @@ describe("ECSqlStatement", () => { `); assert.isTrue(ecdb.isOpen); - const r = await ecdb.withStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(20,TIMESTAMP '2018-10-18T12:00:00Z',20)", async (stmt: ECSqlStatement) => { + const r = await ecdb.withCachedWriteStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(20,TIMESTAMP '2018-10-18T12:00:00Z',20)", async (stmt: ECSqlWriteStatement) => { return stmt.stepForInsert(); }); ecdb.saveChanges(); @@ -87,10 +87,10 @@ describe("ECSqlStatement", () => { `); assert.isTrue(ecdb.isOpen); - await ecdb.withStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(20,TIMESTAMP '2018-10-18T12:00:00Z',20)", async (stmt: ECSqlStatement) => { + await ecdb.withCachedWriteStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(20,TIMESTAMP '2018-10-18T12:00:00Z',20)", async (stmt: ECSqlWriteStatement) => { stmt.stepForInsert(); }); - await ecdb.withStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(30,TIMESTAMP '2019-10-18T12:00:00Z',30)", async (stmt: ECSqlStatement) => { + await ecdb.withCachedWriteStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(30,TIMESTAMP '2019-10-18T12:00:00Z',30)", async (stmt: ECSqlWriteStatement) => { stmt.stepForInsert(); }); ecdb.saveChanges(); @@ -108,6 +108,7 @@ describe("ECSqlStatement", () => { it("null string accessor", async () => { using ecdb = ECDbTestHelper.createECDb(outDir, "nullstring.ecdb"); assert.isTrue(ecdb.isOpen); + // eslint-disable-next-line @typescript-eslint/no-deprecated await ecdb.withPreparedStatement(`VALUES(NULL)`, async (stmt: ECSqlStatement) => { stmt.step(); const str = stmt.getValue(0).getString(); @@ -125,7 +126,7 @@ describe("ECSqlStatement", () => { const ROW_COUNT = 27; // insert test rows for (let i = 1; i <= ROW_COUNT; i++) { - const r = await ecdb.withStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlStatement) => { + const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlWriteStatement) => { return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); @@ -164,7 +165,7 @@ describe("ECSqlStatement", () => { const ROW_COUNT = 100; // insert test rows for (let i = 1; i <= ROW_COUNT; i++) { - const r = await ecdb.withStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlStatement) => { + const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlWriteStatement) => { return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); @@ -195,7 +196,7 @@ describe("ECSqlStatement", () => { const ROW_COUNT = 100; // insert test rows for (let i = 1; i <= ROW_COUNT; i++) { - const r = await ecdb.withStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlStatement) => { + const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlWriteStatement) => { return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); @@ -251,7 +252,7 @@ describe("ECSqlStatement", () => { const ROW_COUNT = 27; // insert test rows for (let i = 1; i <= ROW_COUNT; i++) { - const r = await ecdb.withStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlStatement) => { + const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlWriteStatement) => { return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); @@ -278,7 +279,7 @@ describe("ECSqlStatement", () => { `); assert.isTrue(ecdb.isOpen); for (let i = 1; i <= 5; i++) { - const r = await ecdb.withPreparedStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlStatement) => { + const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlWriteStatement) => { return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); @@ -309,7 +310,7 @@ describe("ECSqlStatement", () => { `); assert.isTrue(ecdb.isOpen); for (let i = 1; i <= 2; i++) { - const r = await ecdb.withStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlStatement) => { + const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlWriteStatement) => { return stmt.stepForInsert(); }); assert.equal(r.status, DbResult.BE_SQLITE_DONE); @@ -331,7 +332,7 @@ describe("ECSqlStatement", () => { const maxRows = 10; const guids: GuidString[] = []; for (let i = 0; i < maxRows; i++) { - const r = await ecdb.withPreparedStatement(`insert into ts.Foo(guid) values(?)`, async (stmt: ECSqlStatement) => { + const r = await ecdb.withCachedWriteStatement(`insert into ts.Foo(guid) values(?)`, async (stmt: ECSqlWriteStatement) => { guids.push(Guid.createValue()); stmt.bindGuid(1, guids[i]); return stmt.stepForInsert(); @@ -422,6 +423,7 @@ describe("ECSqlStatement", () => { 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: ECSqlStatement) => { stmt.bindId(1, expectedId); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -438,7 +440,7 @@ describe("ECSqlStatement", () => { }; let expectedId = Id64.fromLocalAndBriefcaseIds(4444, 0); - let r: ECSqlInsertResult = ecdb.withPreparedStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(?,?)", (stmt: ECSqlStatement) => { + let r: ECSqlInsertResult = ecdb.withCachedWriteStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(?,?)", (stmt: ECSqlWriteStatement) => { stmt.bindId(1, expectedId); stmt.bindString(2, "4444.txt"); return stmt.stepForInsert(); @@ -446,7 +448,7 @@ describe("ECSqlStatement", () => { await verify(ecdb, r, expectedId); expectedId = Id64.fromLocalAndBriefcaseIds(4445, 0); - r = ecdb.withPreparedStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(:id,:name)", (stmt: ECSqlStatement) => { + r = ecdb.withCachedWriteStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(:id,:name)", (stmt: ECSqlWriteStatement) => { stmt.bindId("id", expectedId); stmt.bindString("name", "4445.txt"); @@ -455,14 +457,14 @@ describe("ECSqlStatement", () => { await verify(ecdb, r, expectedId); expectedId = Id64.fromLocalAndBriefcaseIds(4446, 0); - r = ecdb.withPreparedStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(?,?)", (stmt: ECSqlStatement) => { + r = ecdb.withCachedWriteStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(?,?)", (stmt: ECSqlWriteStatement) => { stmt.bindValues([expectedId, "4446.txt"]); return stmt.stepForInsert(); }); await verify(ecdb, r, expectedId); expectedId = Id64.fromLocalAndBriefcaseIds(4447, 0); - r = ecdb.withPreparedStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(:id,:name)", (stmt: ECSqlStatement) => { + r = ecdb.withCachedWriteStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(:id,:name)", (stmt: ECSqlWriteStatement) => { stmt.bindValues({ id: expectedId, name: "4447.txt" }); return stmt.stepForInsert(); }); @@ -500,12 +502,13 @@ describe("ECSqlStatement", () => { `); assert.isTrue(ecdb.isOpen); - const r: ECSqlInsertResult = ecdb.withPreparedStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(20,TIMESTAMP '2018-10-18T12:00:00Z',20)", (stmt: ECSqlStatement) => { + const r: ECSqlInsertResult = ecdb.withCachedWriteStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(20,TIMESTAMP '2018-10-18T12:00:00Z',20)", (stmt: ECSqlWriteStatement) => { 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: ECSqlStatement) => { const nNum: number = 20; const nStr: string = "20"; @@ -601,6 +604,7 @@ describe("ECSqlStatement", () => { }); const ecsqldt = "SELECT 1 FROM ts.Foo WHERE dt=?"; + // eslint-disable-next-line @typescript-eslint/no-deprecated await ecdb.withPreparedStatement(ecsqldt, async (stmt: ECSqlStatement) => { const dtStr: string = "2018-10-18T12:00:00Z"; const num: number = 2458410; @@ -676,6 +680,7 @@ describe("ECSqlStatement", () => { }); const ecsqlfooId = "SELECT 1 FROM ts.Foo WHERE fooId=?"; + // eslint-disable-next-line @typescript-eslint/no-deprecated await ecdb.withPreparedStatement(ecsqlfooId, async (stmt: ECSqlStatement) => { const num: number = 20; const str: string = "20"; @@ -797,7 +802,7 @@ describe("ECSqlStatement", () => { assert.isTrue(ecdb.isOpen); const doubleVal: number = 3.5; - let id = await ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindDouble')", async (stmt: ECSqlStatement) => { + let id = await ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindDouble')", async (stmt: ECSqlWriteStatement) => { stmt.bindDouble(1, doubleVal); stmt.bindDouble(2, doubleVal); stmt.bindDouble(3, doubleVal); @@ -807,6 +812,7 @@ describe("ECSqlStatement", () => { 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: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -825,7 +831,7 @@ describe("ECSqlStatement", () => { }), 1); const smallIntVal: number = 3; - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, small int')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, small int')", (stmt: ECSqlWriteStatement) => { stmt.bindInteger(1, smallIntVal); stmt.bindInteger(2, smallIntVal); stmt.bindInteger(3, smallIntVal); @@ -835,6 +841,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT D,I,L,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -857,7 +864,7 @@ describe("ECSqlStatement", () => { const largeUnsafeNumberStr: string = "12312312312312323654"; const largeUnsafeNumberHexStr: string = "0xaade1ed08b0b5e46"; - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large unsafe number as string')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large unsafe number as string')", (stmt: ECSqlWriteStatement) => { stmt.bindInteger(1, largeUnsafeNumberStr); stmt.bindInteger(2, largeUnsafeNumberStr); stmt.bindInteger(3, largeUnsafeNumberStr); @@ -867,6 +874,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Str(I) si, HexStr(I) hi, Str(L) sl, HexStr(L) hl FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -884,7 +892,7 @@ describe("ECSqlStatement", () => { // assert.equal(row.hl, largeUnsafeNumberHexStr); // }), 1); - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large unsafe number as hexstring')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large unsafe number as hexstring')", (stmt: ECSqlWriteStatement) => { stmt.bindInteger(1, largeUnsafeNumberHexStr); stmt.bindInteger(2, largeUnsafeNumberHexStr); stmt.bindInteger(3, largeUnsafeNumberHexStr); @@ -894,6 +902,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Str(I) si, HexStr(I) hi, Str(L) sl, HexStr(L) hl FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -911,7 +920,7 @@ describe("ECSqlStatement", () => { // assert.equal(row.hl, largeUnsafeNumberHexStr); // }), 1); - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindString, large unsafe number as string')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindString, large unsafe number as string')", (stmt: ECSqlWriteStatement) => { stmt.bindString(1, largeUnsafeNumberStr); stmt.bindString(2, largeUnsafeNumberStr); stmt.bindString(3, largeUnsafeNumberStr); @@ -922,6 +931,7 @@ describe("ECSqlStatement", () => { }); // uint64 cannot be bound as string in SQLite. They get converted to reals + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT D,I,L,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -939,7 +949,7 @@ describe("ECSqlStatement", () => { assert.equal(row.s, largeUnsafeNumberStr); }), 1); - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindString, large unsafe number as hexstring')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindString, large unsafe number as hexstring')", (stmt: ECSqlWriteStatement) => { stmt.bindString(1, largeUnsafeNumberHexStr); stmt.bindString(2, largeUnsafeNumberHexStr); stmt.bindString(3, largeUnsafeNumberHexStr); @@ -949,6 +959,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT CAST(D AS TEXT) d,CAST(I AS TEXT) i,CAST(L AS TEXT) l,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -970,7 +981,7 @@ describe("ECSqlStatement", () => { assert.isFalse(Number.isSafeInteger(largeNegUnsafeNumber)); const largeNegUnsafeNumberStr: string = "-123123123123123236"; - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large negative unsafe number as string')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large negative unsafe number as string')", (stmt: ECSqlWriteStatement) => { stmt.bindInteger(1, largeNegUnsafeNumberStr); stmt.bindInteger(2, largeNegUnsafeNumberStr); stmt.bindInteger(3, largeNegUnsafeNumberStr); @@ -980,6 +991,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT CAST(I AS TEXT) i, CAST(L AS TEXT) l,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -995,7 +1007,7 @@ describe("ECSqlStatement", () => { assert.equal(row.s, largeNegUnsafeNumberStr); }), 1); - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindString, large negative unsafe number as string')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindString, large negative unsafe number as string')", (stmt: ECSqlWriteStatement) => { stmt.bindString(1, largeNegUnsafeNumberStr); stmt.bindString(2, largeNegUnsafeNumberStr); stmt.bindString(3, largeNegUnsafeNumberStr); @@ -1005,6 +1017,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT CAST(I AS TEXT) i, CAST(L AS TEXT) l,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1025,7 +1038,7 @@ describe("ECSqlStatement", () => { const largeSafeNumberStr: string = largeSafeNumber.toString(); const largeSafeNumberHexStr: string = "0x45fcc5c2c8500"; - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large safe number')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large safe number')", (stmt: ECSqlWriteStatement) => { stmt.bindInteger(1, largeSafeNumber); stmt.bindInteger(2, largeSafeNumber); stmt.bindInteger(3, largeSafeNumber); @@ -1035,6 +1048,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT D,I, Str(I) si, HexStr(I) hi, L, Str(L) sl, HexStr(L) hl,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1060,7 +1074,7 @@ describe("ECSqlStatement", () => { // assert.equal(row.s, largeSafeNumberStr); // }); - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large safe number as string')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large safe number as string')", (stmt: ECSqlWriteStatement) => { stmt.bindInteger(1, largeSafeNumberStr); stmt.bindInteger(2, largeSafeNumberStr); stmt.bindInteger(3, largeSafeNumberStr); @@ -1070,6 +1084,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT D,I,L,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1087,7 +1102,7 @@ describe("ECSqlStatement", () => { assert.equal(row.s, largeSafeNumberStr); }), 1); - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large safe number as hexstring')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large safe number as hexstring')", (stmt: ECSqlWriteStatement) => { stmt.bindInteger(1, largeSafeNumberHexStr); stmt.bindInteger(2, largeSafeNumberHexStr); stmt.bindInteger(3, largeSafeNumberHexStr); @@ -1097,6 +1112,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT D,I,L,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1114,7 +1130,7 @@ describe("ECSqlStatement", () => { assert.equal(row.s, largeSafeNumberStr); // even though it was bound as hex str, it gets converted to int64 before persisting }), 1); - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindString, large safe number as string')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindString, large safe number as string')", (stmt: ECSqlWriteStatement) => { stmt.bindString(1, largeSafeNumberStr); stmt.bindString(2, largeSafeNumberStr); stmt.bindString(3, largeSafeNumberStr); @@ -1124,6 +1140,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT D,I,L,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1142,7 +1159,7 @@ describe("ECSqlStatement", () => { }), 1); // SQLite does not parse hex strs bound as strings. - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindString, large safe number as hexstring')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindString, large safe number as hexstring')", (stmt: ECSqlWriteStatement) => { stmt.bindString(1, largeSafeNumberHexStr); stmt.bindString(2, largeSafeNumberHexStr); stmt.bindString(3, largeSafeNumberHexStr); @@ -1152,6 +1169,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT CAST(D AS TEXT) d,CAST(I AS TEXT) i,CAST(L AS TEXT) l,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1173,7 +1191,7 @@ describe("ECSqlStatement", () => { assert.isTrue(Number.isSafeInteger(largeNegSafeNumber)); const largeNegSafeNumberStr: string = largeNegSafeNumber.toString(); - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large negative safe number')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large negative safe number')", (stmt: ECSqlWriteStatement) => { stmt.bindInteger(1, largeNegSafeNumber); stmt.bindInteger(2, largeNegSafeNumber); stmt.bindInteger(3, largeNegSafeNumber); @@ -1183,6 +1201,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT D,I,L,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1200,7 +1219,7 @@ describe("ECSqlStatement", () => { assert.equal(row.s, largeNegSafeNumberStr); }), 1); - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large negative safe number as string')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindInteger, large negative safe number as string')", (stmt: ECSqlWriteStatement) => { stmt.bindInteger(1, largeNegSafeNumberStr); stmt.bindInteger(2, largeNegSafeNumberStr); stmt.bindInteger(3, largeNegSafeNumberStr); @@ -1210,6 +1229,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT D,I,L,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1227,7 +1247,7 @@ describe("ECSqlStatement", () => { assert.equal(row.s, largeNegSafeNumberStr); }); - id = ecdb.withPreparedStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindString, large negative safe number as string')", (stmt: ECSqlStatement) => { + id = ecdb.withCachedWriteStatement("INSERT INTO Test.Foo(D,I,L,S,Description) VALUES(?,?,?,?,'bindString, large negative safe number as string')", (stmt: ECSqlWriteStatement) => { stmt.bindString(1, largeNegSafeNumberStr); stmt.bindString(2, largeNegSafeNumberStr); stmt.bindString(3, largeNegSafeNumberStr); @@ -1237,6 +1257,7 @@ describe("ECSqlStatement", () => { return r.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT D,I,L,S FROM Test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1296,6 +1317,7 @@ describe("ECSqlStatement", () => { const strVal: string = "Hello world"; const verify = async (expectedId: Id64String) => { + // eslint-disable-next-line @typescript-eslint/no-deprecated await ecdb.withPreparedStatement("SELECT Bl,Bo,D,Dt,I,P2d,P3d,S,Struct.Bl s_bl,Struct.Bo s_bo,Struct.D s_d,Struct.Dt s_dt,Struct.I s_i,Struct.P2d s_p2d,Struct.P3d s_p3d,Struct.S s_s FROM test.Foo WHERE ECInstanceId=?", async (stmt: ECSqlStatement) => { stmt.bindId(1, expectedId); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1359,6 +1381,7 @@ describe("ECSqlStatement", () => { }; const ids = new Array(); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("INSERT INTO test.Foo(Bl,Bo,D,Dt,I,P2d,P3d,S,Struct.Bl,Struct.Bo,Struct.D,Struct.Dt,Struct.I,Struct.P2d,Struct.P3d,Struct.S) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", (stmt: ECSqlStatement) => { stmt.bindBlob(1, blobVal); stmt.bindBoolean(2, boolVal); @@ -1389,6 +1412,7 @@ describe("ECSqlStatement", () => { ids.push(res.id!); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("INSERT INTO test.Foo(Bl,Bo,D,Dt,I,P2d,P3d,S,Struct.Bl,Struct.Bo,Struct.D,Struct.Dt,Struct.I,Struct.P2d,Struct.P3d,Struct.S) VALUES(:bl,:bo,:d,:dt,:i,:p2d,:p3d,:s,:s_bl,:s_bo,:s_d,:s_dt,:s_i,:s_p2d,:s_p3d,:s_s)", (stmt: ECSqlStatement) => { stmt.bindBlob("bl", blobVal); stmt.bindBoolean("bo", boolVal); @@ -1458,6 +1482,7 @@ describe("ECSqlStatement", () => { }; const verify = async (expectedId: Id64String) => { + // eslint-disable-next-line @typescript-eslint/no-deprecated await ecdb.withPreparedStatement("SELECT Struct FROM test.Foo WHERE ECInstanceId=?", async (stmt: ECSqlStatement) => { stmt.bindId(1, expectedId); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1489,7 +1514,7 @@ describe("ECSqlStatement", () => { }), 1); }); }; - await ecdb.withPreparedStatement("INSERT INTO test.Foo(Struct) VALUES(?)", async (stmt: ECSqlStatement) => { + await ecdb.withCachedWriteStatement("INSERT INTO test.Foo(Struct) VALUES(?)", async (stmt: ECSqlWriteStatement) => { stmt.bindStruct(1, structVal); const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); @@ -1497,7 +1522,7 @@ describe("ECSqlStatement", () => { await verify(res.id!); }); - await ecdb.withPreparedStatement("INSERT INTO test.Foo(Struct) VALUES(?)", async (stmt: ECSqlStatement) => { + await ecdb.withCachedWriteStatement("INSERT INTO test.Foo(Struct) VALUES(?)", async (stmt: ECSqlWriteStatement) => { stmt.bindValues([structVal]); const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); @@ -1505,7 +1530,7 @@ describe("ECSqlStatement", () => { await verify(res.id!); }); - await ecdb.withPreparedStatement("INSERT INTO test.Foo(Struct) VALUES(:str)", async (stmt: ECSqlStatement) => { + await ecdb.withCachedWriteStatement("INSERT INTO test.Foo(Struct) VALUES(:str)", async (stmt: ECSqlWriteStatement) => { stmt.bindStruct("str", structVal); const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); @@ -1513,7 +1538,7 @@ describe("ECSqlStatement", () => { await verify(res.id!); }); - await ecdb.withPreparedStatement("INSERT INTO test.Foo(Struct) VALUES(:str)", async (stmt: ECSqlStatement) => { + await ecdb.withCachedWriteStatement("INSERT INTO test.Foo(Struct) VALUES(:str)", async (stmt: ECSqlWriteStatement) => { stmt.bindValues({ str: structVal }); const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); @@ -1543,6 +1568,7 @@ describe("ECSqlStatement", () => { const addressArray = [{ city: "London", zip: 10000 }, { city: "Manchester", zip: 20000 }, { city: "Edinburgh", zip: 30000 }]; const verify = async (expectedId: Id64String) => { + // eslint-disable-next-line @typescript-eslint/no-deprecated await ecdb.withPreparedStatement("SELECT I_Array, Dt_Array, Addresses FROM test.Foo WHERE ECInstanceId=?", async (stmt: ECSqlStatement) => { stmt.bindId(1, expectedId); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1586,7 +1612,7 @@ describe("ECSqlStatement", () => { }), 1); }; - await ecdb.withPreparedStatement("INSERT INTO test.Foo(I_Array,Dt_Array,Addresses) VALUES(?,?,?)", async (stmt: ECSqlStatement) => { + await ecdb.withCachedWriteStatement("INSERT INTO test.Foo(I_Array,Dt_Array,Addresses) VALUES(?,?,?)", async (stmt: ECSqlWriteStatement) => { stmt.bindArray(1, intArray); stmt.bindArray(2, dtArray); stmt.bindArray(3, addressArray); @@ -1596,7 +1622,7 @@ describe("ECSqlStatement", () => { await verify(res.id!); }); - await ecdb.withPreparedStatement("INSERT INTO test.Foo(I_Array,Dt_Array,Addresses) VALUES(?,?,?)", async (stmt: ECSqlStatement) => { + await ecdb.withCachedWriteStatement("INSERT INTO test.Foo(I_Array,Dt_Array,Addresses) VALUES(?,?,?)", async (stmt: ECSqlWriteStatement) => { stmt.bindValues([intArray, dtArray, addressArray]); const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); @@ -1604,7 +1630,7 @@ describe("ECSqlStatement", () => { await verify(res.id!); }); - await ecdb.withPreparedStatement("INSERT INTO test.Foo(I_Array,Dt_Array,Addresses) VALUES(:iarray,:dtarray,:addresses)", async (stmt: ECSqlStatement) => { + await ecdb.withCachedWriteStatement("INSERT INTO test.Foo(I_Array,Dt_Array,Addresses) VALUES(:iarray,:dtarray,:addresses)", async (stmt: ECSqlWriteStatement) => { stmt.bindArray("iarray", intArray); stmt.bindArray("dtarray", dtArray); stmt.bindArray("addresses", addressArray); @@ -1614,7 +1640,7 @@ describe("ECSqlStatement", () => { await verify(res.id!); }); - await ecdb.withPreparedStatement("INSERT INTO test.Foo(I_Array,Dt_Array,Addresses) VALUES(:iarray,:dtarray,:addresses)", async (stmt: ECSqlStatement) => { + await ecdb.withCachedWriteStatement("INSERT INTO test.Foo(I_Array,Dt_Array,Addresses) VALUES(:iarray,:dtarray,:addresses)", async (stmt: ECSqlWriteStatement) => { stmt.bindValues({ iarray: intArray, dtarray: dtArray, addresses: addressArray }); const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); @@ -1645,7 +1671,7 @@ describe("ECSqlStatement", () => { assert.isTrue(ecdb.isOpen); - const parentId: Id64String = ecdb.withStatement("INSERT INTO test.Parent(Code) VALUES('Parent 1')", (stmt: ECSqlStatement) => { + const parentId: Id64String = ecdb.withCachedWriteStatement("INSERT INTO test.Parent(Code) VALUES('Parent 1')", (stmt: ECSqlWriteStatement) => { const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); assert.isDefined(res.id); @@ -1653,7 +1679,7 @@ describe("ECSqlStatement", () => { }); const childIds = new Array(); - ecdb.withStatement("INSERT INTO test.Child(Name,Parent) VALUES(?,?)", (stmt: ECSqlStatement) => { + ecdb.withCachedWriteStatement("INSERT INTO test.Child(Name,Parent) VALUES(?,?)", (stmt: ECSqlWriteStatement) => { stmt.bindString(1, "Child 1"); stmt.bindNavigation(2, { id: parentId, relClassName: "Test.ParentHasChildren" }); let res: ECSqlInsertResult = stmt.stepForInsert(); @@ -1671,7 +1697,7 @@ describe("ECSqlStatement", () => { childIds.push(res.id!); }); - ecdb.withStatement("INSERT INTO test.Child(Name,Parent) VALUES(:name,:parent)", (stmt: ECSqlStatement) => { + ecdb.withCachedWriteStatement("INSERT INTO test.Child(Name,Parent) VALUES(:name,:parent)", (stmt: ECSqlWriteStatement) => { stmt.bindString("name", "Child 3"); stmt.bindNavigation("parent", { id: parentId, relClassName: "Test.ParentHasChildren" }); let res: ECSqlInsertResult = stmt.stepForInsert(); @@ -1689,6 +1715,7 @@ describe("ECSqlStatement", () => { childIds.push(res.id!); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Name,Parent FROM test.Child ORDER BY Name", (stmt: ECSqlStatement) => { let rowCount: number = 0; while (stmt.step() === DbResult.BE_SQLITE_ROW) { @@ -1715,13 +1742,17 @@ describe("ECSqlStatement", () => { it("should bind Range3d for parameter in spatial sql function", async () => { const iModel = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile("ECSqlStatement", "BindRange3d.bim"), { rootSubject: { name: "BindRange3d" } }); try { + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT e.ECInstanceId FROM bis.Element e, bis.SpatialIndex rt WHERE rt.ECInstanceId MATCH DGN_spatial_overlap_aabb(?) AND e.ECInstanceId=rt.ECInstanceId", + // eslint-disable-next-line @typescript-eslint/no-deprecated (stmt: ECSqlStatement) => { stmt.bindRange3d(1, new Range3d(0.0, 0.0, 0.0, 1000.0, 1000.0, 1000.0)); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT e.ECInstanceId FROM bis.Element e, bis.SpatialIndex rt WHERE rt.ECInstanceId MATCH DGN_spatial_overlap_aabb(?) AND e.ECInstanceId=rt.ECInstanceId", + // eslint-disable-next-line @typescript-eslint/no-deprecated (stmt: ECSqlStatement) => { stmt.bindValues([new Range3d(0.0, 0.0, 0.0, 1000.0, 1000.0, 1000.0)]); assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); @@ -1743,7 +1774,7 @@ describe("ECSqlStatement", () => { assert.isTrue(ecdb.isOpen); - const id: Id64String = ecdb.withPreparedStatement("INSERT INTO test.Foo([Range3d]) VALUES(?)", (stmt: ECSqlStatement) => { + const id: Id64String = ecdb.withCachedWriteStatement("INSERT INTO test.Foo([Range3d]) VALUES(?)", (stmt: ECSqlWriteStatement) => { stmt.bindRange3d(1, testRange); const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); @@ -1751,6 +1782,7 @@ describe("ECSqlStatement", () => { return res.id!; }); ecdb.saveChanges(); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT [Range3d] FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -1767,7 +1799,7 @@ describe("ECSqlStatement", () => { assert.isTrue(ecdb.isOpen); const idNumbers: number[] = [4444, 4545, 1234, 6758, 1312]; - ecdb.withPreparedStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(?,?)", (stmt: ECSqlStatement) => { + ecdb.withCachedWriteStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(?,?)", (stmt: ECSqlWriteStatement) => { idNumbers.forEach((idNum: number) => { const expectedId = Id64.fromLocalAndBriefcaseIds(idNum, 0); stmt.bindId(1, expectedId); @@ -1778,6 +1810,7 @@ describe("ECSqlStatement", () => { assert.equal(r.id!, expectedId); ecdb.saveChanges(); + // eslint-disable-next-line @typescript-eslint/no-deprecated 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(); @@ -1790,6 +1823,7 @@ describe("ECSqlStatement", () => { }); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT ECInstanceId, ECClassId, Name from ecdbf.ExternalFileInfo WHERE InVirtualSet(?, ECInstanceId)", (stmt: ECSqlStatement) => { let idSet: Id64String[] = []; stmt.bindIdSet(1, idSet); @@ -1822,117 +1856,121 @@ describe("ECSqlStatement", () => { it("should bind IdSets to IdSet Virtual Table", async () => { using ecdb = ECDbTestHelper.createECDb(outDir, "bindids.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(); - }); - }); + assert.isTrue(ecdb.isOpen); - ecdb.withPreparedStatement("SELECT ECInstanceId, ECClassId, Name from ecdbf.ExternalFileInfo, 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(); + const idNumbers: number[] = [4444, 4545, 1234, 6758, 1312]; + ecdb.withCachedWriteStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(?,?)", (stmt: ECSqlWriteStatement) => { + 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(); - 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`); + // eslint-disable-next-line @typescript-eslint/no-deprecated + 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(); - - 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`); }); + }); + + // eslint-disable-next-line @typescript-eslint/no-deprecated + ecdb.withPreparedStatement("SELECT ECInstanceId, ECClassId, Name from ecdbf.ExternalFileInfo, 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 () => { using ecdb = ECDbTestHelper.createECDb(outDir, "bindids.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(); - }); - }); + assert.isTrue(ecdb.isOpen); - ecdb.withPreparedStatement("SELECT ECInstanceId, ECClassId, Name from ecdbf.ExternalFileInfo, 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(); + const idNumbers: number[] = [4444, 4545, 1234, 6758, 1312]; + ecdb.withCachedWriteStatement("INSERT INTO ecdbf.ExternalFileInfo(ECInstanceId,Name) VALUES(?,?)", (stmt: ECSqlWriteStatement) => { + 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(); - 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); + // eslint-disable-next-line @typescript-eslint/no-deprecated + 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(); - - 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); }); + }); + + // eslint-disable-next-line @typescript-eslint/no-deprecated + 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 @@ -1954,11 +1992,10 @@ describe("ECSqlStatement", () => { assert.isTrue(ecdb.isOpen); - let id1: Id64String, id2: Id64String; + let id1: Id64String = "", id2: Id64String = ""; // *** test without statement cache - { - using stmt = ecdb.prepareStatement("INSERT INTO test.Person(Name,Age,Location) VALUES(?,?,?)"); + ecdb.withCachedWriteStatement("INSERT INTO test.Person(Name,Age,Location) VALUES(?,?,?)", (stmt: ECSqlWriteStatement) => { stmt.bindString(1, "Mary Miller"); stmt.bindInteger(2, 30); stmt.bindStruct(3, { Street: "2000 Main Street", City: "New York", Zip: 12311 }); @@ -1968,10 +2005,11 @@ describe("ECSqlStatement", () => { assert.isDefined(res.id); id1 = res.id!; assert.isTrue(Id64.isValidId64(id1)); - } + }); + // *** test withstatement cache - ecdb.withPreparedStatement("INSERT INTO test.Person(Name,Age,Location) VALUES(?,?,?)", (stmt: ECSqlStatement) => { + ecdb.withCachedWriteStatement("INSERT INTO test.Person(Name,Age,Location) VALUES(?,?,?)", (stmt: ECSqlWriteStatement) => { stmt.bindString(1, "Mary Miller"); stmt.bindInteger(2, 30); stmt.bindStruct(3, { Street: "2000 Main Street", City: "New York", Zip: 12311 }); @@ -1984,6 +2022,7 @@ describe("ECSqlStatement", () => { }); { + // eslint-disable-next-line @typescript-eslint/no-deprecated using stmt = ecdb.prepareStatement("SELECT ECInstanceId,ECClassId,Name,Age,Location FROM test.Person ORDER BY ECInstanceId"); let rowCount = 0; while (stmt.step() === DbResult.BE_SQLITE_ROW) { @@ -1992,7 +2031,7 @@ describe("ECSqlStatement", () => { if (rowCount === 1) assert.equal(row.id, id1); else - assert.equal(row.id, id2!); + assert.equal(row.id, id2); assert.equal(row.className, "Test.Person"); assert.equal(row.name, "Mary Miller"); @@ -2047,7 +2086,7 @@ describe("ECSqlStatement", () => { const p3dVal = new Point3d(1, 2, 3); const strVal: string = "Hello world"; - const id: Id64String = ecdb.withPreparedStatement("INSERT INTO test.Foo(Bl,Bo,D,Dt,I,P2d,P3d,S) VALUES(?,?,?,?,?,?,?,?)", (stmt: ECSqlStatement) => { + const id: Id64String = ecdb.withCachedWriteStatement("INSERT INTO test.Foo(Bl,Bo,D,Dt,I,P2d,P3d,S) VALUES(?,?,?,?,?,?,?,?)", (stmt: ECSqlWriteStatement) => { stmt.bindBlob(1, blobVal); stmt.bindBoolean(2, boolVal); stmt.bindDouble(3, doubleVal); @@ -2061,6 +2100,7 @@ describe("ECSqlStatement", () => { return res.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT ECInstanceId, ECClassId, Bl,Bo,D,Dt,I,P2d,P3d,S FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2108,6 +2148,7 @@ describe("ECSqlStatement", () => { // assert.equal(row.s, strVal); // }), 1); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Bl AS Blobby, I+10, Lower(S), Upper(S) CapitalS FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2125,6 +2166,7 @@ describe("ECSqlStatement", () => { // assert.equal(row.capitalS, strVal.toUpperCase()); // }), 1); + // eslint-disable-next-line @typescript-eslint/no-deprecated const testSchemaId: Id64String = ecdb.withPreparedStatement("SELECT ECInstanceId FROM meta.ECSchemaDef WHERE Name='Test'", (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const row = stmt.getRow(); @@ -2132,6 +2174,7 @@ describe("ECSqlStatement", () => { return Id64.fromJSON(row.id); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated const fooClassId: Id64String = ecdb.withPreparedStatement("SELECT ECInstanceId FROM meta.ECClassDef WHERE Name='Foo'", (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const row = stmt.getRow(); @@ -2139,6 +2182,7 @@ describe("ECSqlStatement", () => { return Id64.fromJSON(row.id); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT s.ECInstanceId, c.ECInstanceId, c.Name, s.Name FROM meta.ECClassDef c JOIN meta.ECSchemaDef s ON c.Schema.Id=s.ECInstanceId WHERE s.Name='Test' AND c.Name='Foo'", (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const row = stmt.getRow(); @@ -2151,6 +2195,7 @@ describe("ECSqlStatement", () => { assert.equal(row.id_1, fooClassId); }), 1); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT count(*) cnt FROM meta.ECSchemaDef", (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const row = stmt.getRow(); @@ -2165,6 +2210,7 @@ describe("ECSqlStatement", () => { assert.equal(row.cnt, 6); }), 1); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT 1 FROM meta.ECSchemaDef LIMIT 1", (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const row = stmt.getRow(); @@ -2177,6 +2223,7 @@ describe("ECSqlStatement", () => { assert.equal(row["1"], 1); }), 1); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT NULL FROM meta.ECSchemaDef LIMIT 1", (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const row = stmt.getRow(); @@ -2201,28 +2248,28 @@ describe("ECSqlStatement", () => { const abbreviatedSingleBlobVal = `{"bytes":${singleBlobVal.byteLength}}`; const emptyBlobVal = new Uint8Array(); - const fullId: Id64String = ecdb.withPreparedStatement("INSERT INTO test.Foo(Bl) VALUES(?)", (stmt: ECSqlStatement) => { + const fullId: Id64String = ecdb.withCachedWriteStatement("INSERT INTO test.Foo(Bl) VALUES(?)", (stmt: ECSqlWriteStatement) => { stmt.bindBlob(1, blobVal); const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); return res.id!; }); - const singleId: Id64String = ecdb.withPreparedStatement("INSERT INTO test.Foo(Bl) VALUES(?)", (stmt: ECSqlStatement) => { + const singleId: Id64String = ecdb.withCachedWriteStatement("INSERT INTO test.Foo(Bl) VALUES(?)", (stmt: ECSqlWriteStatement) => { stmt.bindBlob(1, singleBlobVal); const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); return res.id!; }); - const emptyId: Id64String = ecdb.withPreparedStatement("INSERT INTO test.Foo(Bl) VALUES(?)", (stmt: ECSqlStatement) => { + const emptyId: Id64String = ecdb.withCachedWriteStatement("INSERT INTO test.Foo(Bl) VALUES(?)", (stmt: ECSqlWriteStatement) => { stmt.bindBlob(1, emptyBlobVal); const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); return res.id!; }); - const nullId: Id64String = ecdb.withPreparedStatement("INSERT INTO test.Foo(Bl) VALUES(?)", (stmt: ECSqlStatement) => { + const nullId: Id64String = ecdb.withCachedWriteStatement("INSERT INTO test.Foo(Bl) VALUES(?)", (stmt: ECSqlWriteStatement) => { stmt.bindNull(1); const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); @@ -2323,7 +2370,7 @@ describe("ECSqlStatement", () => { `); assert.isTrue(ecdb.isOpen); let rowCount: number; - const parentId: Id64String = ecdb.withPreparedStatement("INSERT INTO test.Parent(Code) VALUES('Parent 1')", (stmt: ECSqlStatement) => { + const parentId: Id64String = ecdb.withCachedWriteStatement("INSERT INTO test.Parent(Code) VALUES('Parent 1')", (stmt: ECSqlWriteStatement) => { const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); assert.isDefined(res.id); @@ -2331,7 +2378,7 @@ describe("ECSqlStatement", () => { }); const childIds = new Array(); - ecdb.withPreparedStatement("INSERT INTO test.Child(Name,Parent) VALUES(?,?)", (stmt: ECSqlStatement) => { + ecdb.withCachedWriteStatement("INSERT INTO test.Child(Name,Parent) VALUES(?,?)", (stmt: ECSqlWriteStatement) => { stmt.bindString(1, "Child 1"); stmt.bindNavigation(2, { id: parentId, relClassName: "Test.ParentHasChildren" }); let res: ECSqlInsertResult = stmt.stepForInsert(); @@ -2349,6 +2396,7 @@ describe("ECSqlStatement", () => { childIds.push(res.id!); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Name,Parent FROM test.Child ORDER BY Name", (stmt: ECSqlStatement) => { rowCount = 0; while (stmt.step() === DbResult.BE_SQLITE_ROW) { @@ -2369,6 +2417,7 @@ describe("ECSqlStatement", () => { assert.equal(row.parent.relClassName, "Test.ParentHasChildren"); }), 2); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Name,Parent.Id,Parent.RelECClassId, Parent.Id myParentId, Parent.RelECClassId myParentRelClassId FROM test.Child ORDER BY Name", (stmt: ECSqlStatement) => { rowCount = 0; while (stmt.step() === DbResult.BE_SQLITE_ROW) { @@ -2394,6 +2443,7 @@ describe("ECSqlStatement", () => { }), 2); const childId: Id64String = childIds[0]; + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT ECInstanceId,ECClassId,SourceECInstanceId,SourceECClassId,TargetECInstanceId,TargetECClassId FROM test.ParentHasChildren WHERE TargetECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, childId); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2415,6 +2465,7 @@ describe("ECSqlStatement", () => { assert.equal(row.targetClassName, "Test.Child"); }), 1); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT ECInstanceId as MyId,ECClassId as MyClassId,SourceECInstanceId As MySourceId,SourceECClassId As MySourceClassId,TargetECInstanceId As MyTargetId,TargetECClassId As MyTargetClassId FROM test.ParentHasChildren WHERE TargetECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, childId); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2468,7 +2519,7 @@ describe("ECSqlStatement", () => { const p3dVal: XYAndZ = { x: 1, y: 2, z: 3 }; const stringVal: string = "Hello World"; - const id: Id64String = ecdb.withPreparedStatement("INSERT INTO test.Foo(Struct) VALUES(?)", (stmt: ECSqlStatement) => { + const id: Id64String = ecdb.withCachedWriteStatement("INSERT INTO test.Foo(Struct) VALUES(?)", (stmt: ECSqlWriteStatement) => { stmt.bindStruct(1, { bl: blobVal, bo: boolVal, d: doubleVal, dt: dtVal, i: intVal, p2d: p2dVal, p3d: p3dVal, s: stringVal }); const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); @@ -2477,6 +2528,7 @@ describe("ECSqlStatement", () => { }); const expectedStruct = { bl: blobVal, bo: boolVal, d: doubleVal, dt: dtVal, i: intVal, p2d: p2dVal, p3d: p3dVal, s: stringVal }; + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Struct FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2508,6 +2560,7 @@ describe("ECSqlStatement", () => { assert.equal(row.struct.s, expectedStruct.s); }), 1); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Struct FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2525,6 +2578,7 @@ describe("ECSqlStatement", () => { assert.equal(actualStruct.s, expectedStruct.s); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Struct.Bl, Struct.Bo, Struct.D, Struct.Dt, Struct.I, Struct.P2d, Struct.P3d, Struct.S FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2588,7 +2642,7 @@ describe("ECSqlStatement", () => { i: 3, l: 12312312312312, p2d: { x: 1, y: 2 }, p3d: { x: 1, y: 2, z: 3 }, s: "Hello World", }; - const id: Id64String = ecdb.withPreparedStatement("INSERT INTO test.Foo(Bl,Bo,D,Dt,I,L,P2d,P3d,S) VALUES(:bl,:bo,:d,:dt,:i,:l,:p2d,:p3d,:s)", (stmt: ECSqlStatement) => { + const id: Id64String = ecdb.withCachedWriteStatement("INSERT INTO test.Foo(Bl,Bo,D,Dt,I,L,P2d,P3d,S) VALUES(:bl,:bo,:d,:dt,:i,:l,:p2d,:p3d,:s)", (stmt: ECSqlWriteStatement) => { stmt.bindValues({ bl: blobVal, bo: expectedRow.bo, d: expectedRow.d, dt: expectedRow.dt, i: expectedRow.i, l: expectedRow.l, p2d: expectedRow.p2d, p3d: expectedRow.p3d, s: expectedRow.s, @@ -2600,6 +2654,7 @@ describe("ECSqlStatement", () => { }); ecdb.saveChanges(); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT I, HexStr(I) hex FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2608,6 +2663,7 @@ describe("ECSqlStatement", () => { assert.equal(row.hex, "0x3"); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT L, HexStr(L) hex FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2616,11 +2672,13 @@ describe("ECSqlStatement", () => { assert.equal(row.hex, "0xb32af0071f8"); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Bl, HexStr(Bl) hex FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ERROR); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Bo, HexStr(Bo) hex FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2629,26 +2687,31 @@ describe("ECSqlStatement", () => { assert.equal(row.hex, "0x1"); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT D, HexStr(D) hex FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ERROR); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Dt, HexStr(Dt) hex FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ERROR); }); // SQL functions cannot take points. So here preparation already fails + // eslint-disable-next-line @typescript-eslint/no-deprecated assert.throw(() => ecdb.withPreparedStatement("SELECT P2d, HexStr(P2d) hex FROM test.Foo WHERE ECInstanceId=?", () => { assert.fail(); })); // SQL functions cannot take points. So here preparation already fails + // eslint-disable-next-line @typescript-eslint/no-deprecated assert.throw(() => ecdb.withPreparedStatement("SELECT P3d, HexStr(P3d) hex FROM test.Foo WHERE ECInstanceId=?", () => { assert.fail(); })); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT S, HexStr(S) hex FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ERROR); @@ -2676,7 +2739,7 @@ describe("ECSqlStatement", () => { `); assert.isTrue(ecdb.isOpen); - const id: Id64String = ecdb.withPreparedStatement("INSERT INTO test.Foo(MyStat,MyStats,MyDomain,MyDomains) VALUES(test.Status.[On],?,test.Domain.Org,?)", (stmt: ECSqlStatement) => { + const id: Id64String = ecdb.withCachedWriteStatement("INSERT INTO test.Foo(MyStat,MyStats,MyDomain,MyDomains) VALUES(test.Status.[On],?,test.Domain.Org,?)", (stmt: ECSqlWriteStatement) => { stmt.bindValue(1, [1, 2]); stmt.bindValue(2, ["Org", "Com"]); const res: ECSqlInsertResult = stmt.stepForInsert(); @@ -2685,6 +2748,7 @@ describe("ECSqlStatement", () => { return res.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT MyStat,MyStats, MyDomain,MyDomains FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2695,16 +2759,19 @@ describe("ECSqlStatement", () => { assert.equal(row.myDomain, "Org"); assert.deepEqual(row.myDomains, ["Org", "Com"]); + // eslint-disable-next-line @typescript-eslint/no-deprecated const myStatVal: ECSqlValue = stmt.getValue(0); assert.isFalse(myStatVal.isNull); assert.isTrue(myStatVal.columnInfo.isEnum()); assert.equal(myStatVal.getInteger(), 1); assert.deepEqual(myStatVal.getEnum(), [{ schema: "Test", name: "Status", key: "On", value: 1 }]); + // eslint-disable-next-line @typescript-eslint/no-deprecated const myStatsVal: ECSqlValue = stmt.getValue(1); assert.isFalse(myStatsVal.isNull); assert.isTrue(myStatsVal.columnInfo.isEnum()); assert.deepEqual(myStatsVal.getArray(), [1, 2]); + // eslint-disable-next-line @typescript-eslint/no-deprecated const actualStatsEnums: ECEnumValue[][] = []; for (const arrayElement of myStatsVal.getArrayIterator()) { actualStatsEnums.push(arrayElement.getEnum()!); @@ -2713,16 +2780,19 @@ describe("ECSqlStatement", () => { assert.deepEqual(actualStatsEnums[0], [{ schema: "Test", name: "Status", key: "On", value: 1 }]); assert.deepEqual(actualStatsEnums[1], [{ schema: "Test", name: "Status", key: "Off", value: 2 }]); + // eslint-disable-next-line @typescript-eslint/no-deprecated const myDomainVal: ECSqlValue = stmt.getValue(2); assert.isFalse(myDomainVal.isNull); assert.isTrue(myDomainVal.columnInfo.isEnum()); assert.equal(myDomainVal.getString(), "Org"); assert.deepEqual(myDomainVal.getEnum(), [{ schema: "Test", name: "Domain", key: "Org", value: "Org" }]); + // eslint-disable-next-line @typescript-eslint/no-deprecated const myDomainsVal: ECSqlValue = stmt.getValue(3); assert.isFalse(myDomainsVal.isNull); assert.isTrue(myDomainsVal.columnInfo.isEnum()); assert.deepEqual(myDomainsVal.getArray(), ["Org", "Com"]); + // eslint-disable-next-line @typescript-eslint/no-deprecated const actualDomainsEnums: ECEnumValue[][] = []; for (const arrayElement of myDomainsVal.getArrayIterator()) { actualDomainsEnums.push(arrayElement.getEnum()!); @@ -2740,18 +2810,21 @@ describe("ECSqlStatement", () => { }), 1); // test some enums in the built-in schemas + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT Type,Modifier FROM meta.ECClassDef WHERE Name='Foo'", (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); // getRow just returns the enum values const row: any = stmt.getRow(); assert.deepEqual(row, { type: 0, modifier: 2 }); + // eslint-disable-next-line @typescript-eslint/no-deprecated const typeVal: ECSqlValue = stmt.getValue(0); assert.isFalse(typeVal.isNull); assert.isTrue(typeVal.columnInfo.isEnum()); assert.equal(typeVal.getInteger(), 0); assert.deepEqual(typeVal.getEnum(), [{ schema: "ECDbMeta", name: "ECClassType", key: "Entity", value: 0 }]); + // eslint-disable-next-line @typescript-eslint/no-deprecated const modifierVal: ECSqlValue = stmt.getValue(1); assert.isFalse(modifierVal.isNull); assert.isTrue(modifierVal.columnInfo.isEnum()); @@ -2788,7 +2861,7 @@ describe("ECSqlStatement", () => { `); assert.isTrue(ecdb.isOpen); - const ids: { unored: Id64String, ored: Id64String, unmatched: Id64String } = ecdb.withPreparedStatement("INSERT INTO test.Foo(MyColor,MyDomain) VALUES(?,?)", (stmt: ECSqlStatement) => { + const ids: { unored: Id64String, ored: Id64String, unmatched: Id64String } = ecdb.withCachedWriteStatement("INSERT INTO test.Foo(MyColor,MyDomain) VALUES(?,?)", (stmt: ECSqlWriteStatement) => { stmt.bindValue(1, 4); stmt.bindValue(2, "com"); let res: ECSqlInsertResult = stmt.stepForInsert(); @@ -2819,6 +2892,7 @@ describe("ECSqlStatement", () => { return { unored, ored, unmatched }; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT MyColor,MyDomain FROM test.Foo WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, ids.unored); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2827,12 +2901,14 @@ describe("ECSqlStatement", () => { assert.equal(row.myColor, 4); assert.equal(row.myDomain, "com"); + // eslint-disable-next-line @typescript-eslint/no-deprecated let colVal: ECSqlValue = stmt.getValue(0); assert.isFalse(colVal.isNull); assert.isTrue(colVal.columnInfo.isEnum()); assert.equal(colVal.getInteger(), 4); assert.deepEqual(colVal.getEnum(), [{ schema: "Test", name: "Color", key: "Blue", value: 4 }]); + // eslint-disable-next-line @typescript-eslint/no-deprecated let domainVal: ECSqlValue = stmt.getValue(1); assert.isFalse(domainVal.isNull); assert.isTrue(domainVal.columnInfo.isEnum()); @@ -2899,11 +2975,13 @@ describe("ECSqlStatement", () => { }), 1); // test some enums in the built-in schemas + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT CustomAttributeContainerType caType FROM meta.ECClassDef WHERE Type=meta.ECClassType.CustomAttribute AND Name='DateTimeInfo'", (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const row: any = stmt.getRow(); assert.equal(row.caType, 160); + // eslint-disable-next-line @typescript-eslint/no-deprecated const caTypeVal: ECSqlValue = stmt.getValue(0); assert.isFalse(caTypeVal.isNull); assert.isTrue(caTypeVal.columnInfo.isEnum()); @@ -2929,7 +3007,7 @@ describe("ECSqlStatement", () => { `); assert.isTrue(ecdb.isOpen); - const r = await ecdb.withPreparedStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(20,TIMESTAMP '2018-10-18T12:00:00Z',20)", async (stmt: ECSqlStatement) => { + const r = await ecdb.withCachedWriteStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(20,TIMESTAMP '2018-10-18T12:00:00Z',20)", async (stmt: ECSqlWriteStatement) => { const nativesql: string = stmt.getNativeSql(); assert.isTrue(nativesql.startsWith("INSERT INTO [ts_Foo]")); return stmt.stepForInsert(); @@ -2948,13 +3026,14 @@ describe("ECSqlStatement", () => { `); assert.isTrue(ecdb.isOpen); - const id: Id64String = ecdb.withPreparedStatement("INSERT INTO test.MyClass(MyProperty) VALUES('Value')", (stmt: ECSqlStatement) => { + const id: Id64String = ecdb.withCachedWriteStatement("INSERT INTO test.MyClass(MyProperty) VALUES('Value')", (stmt: ECSqlWriteStatement) => { const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); assert.isDefined(res.id); return res.id!; }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT MyProperty as MyAlias, 1 as MyGenerated FROM test.MyClass WHERE ECInstanceId=?", (stmt: ECSqlStatement) => { stmt.bindId(1, id); assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); @@ -2963,7 +3042,9 @@ describe("ECSqlStatement", () => { assert.equal(row.myAlias, "Value"); assert.equal(row.myGenerated, 1); + // eslint-disable-next-line @typescript-eslint/no-deprecated const val0: ECSqlValue = stmt.getValue(0); + // eslint-disable-next-line @typescript-eslint/no-deprecated const colInfo0: ECSqlColumnInfo = val0.columnInfo; assert.equal(colInfo0.getPropertyName(), "MyAlias"); @@ -2973,7 +3054,9 @@ describe("ECSqlStatement", () => { assert.isDefined(originPropertyName); assert.equal(originPropertyName, "MyProperty"); + // eslint-disable-next-line @typescript-eslint/no-deprecated const val1: ECSqlValue = stmt.getValue(1); + // eslint-disable-next-line @typescript-eslint/no-deprecated const colInfo1: ECSqlColumnInfo = val1.columnInfo; assert.equal(colInfo1.getPropertyName(), "MyGenerated"); @@ -3018,12 +3101,13 @@ describe("ECSqlStatement", () => { `); assert.isTrue(ecdb.isOpen); - ecdb.withPreparedStatement("INSERT INTO Test.A (f.c.a, f.c.b, f.d, g) VALUES ('f.c.a' ,'f.c.b', 'f.d', 'g')", (stmt: ECSqlStatement) => { + ecdb.withCachedWriteStatement("INSERT INTO Test.A (f.c.a, f.c.b, f.d, g) VALUES ('f.c.a' ,'f.c.b', 'f.d', 'g')", (stmt: ECSqlWriteStatement) => { const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); assert.isDefined(res.id); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT f, f.c.a, f.c.b, f.d, g FROM Test.A", (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); // getRow just returns the enum values @@ -3033,7 +3117,9 @@ describe("ECSqlStatement", () => { assert.equal(row.f.d, "f.d"); assert.equal(row.g, "g"); + // eslint-disable-next-line @typescript-eslint/no-deprecated const val0: ECSqlValue = stmt.getValue(0); + // eslint-disable-next-line @typescript-eslint/no-deprecated const colInfo0: ECSqlColumnInfo = val0.columnInfo; assert.equal(colInfo0.getPropertyName(), "f"); @@ -3043,7 +3129,9 @@ describe("ECSqlStatement", () => { assert.isDefined(originPropertyName0); assert.equal(originPropertyName0, "f"); + // eslint-disable-next-line @typescript-eslint/no-deprecated const val1: ECSqlValue = stmt.getValue(1); + // eslint-disable-next-line @typescript-eslint/no-deprecated const colInfo1: ECSqlColumnInfo = val1.columnInfo; assert.equal(colInfo1.getPropertyName(), "a"); @@ -3053,7 +3141,9 @@ describe("ECSqlStatement", () => { assert.isDefined(originPropertyName1); assert.equal(originPropertyName1, "a"); + // eslint-disable-next-line @typescript-eslint/no-deprecated const val2: ECSqlValue = stmt.getValue(2); + // eslint-disable-next-line @typescript-eslint/no-deprecated const colInfo2: ECSqlColumnInfo = val2.columnInfo; assert.equal(colInfo2.getPropertyName(), "b"); @@ -3063,7 +3153,9 @@ describe("ECSqlStatement", () => { assert.isDefined(originPropertyName2); assert.equal(originPropertyName2, "b"); + // eslint-disable-next-line @typescript-eslint/no-deprecated const val3: ECSqlValue = stmt.getValue(3); + // eslint-disable-next-line @typescript-eslint/no-deprecated const colInfo3: ECSqlColumnInfo = val3.columnInfo; assert.equal(colInfo3.getPropertyName(), "d"); @@ -3073,7 +3165,9 @@ describe("ECSqlStatement", () => { assert.isDefined(originPropertyName3); assert.equal(originPropertyName3, "d"); + // eslint-disable-next-line @typescript-eslint/no-deprecated const val4: ECSqlValue = stmt.getValue(4); + // eslint-disable-next-line @typescript-eslint/no-deprecated const colInfo4: ECSqlColumnInfo = val4.columnInfo; assert.equal(colInfo4.getPropertyName(), "g"); @@ -3084,12 +3178,13 @@ describe("ECSqlStatement", () => { assert.equal(originPropertyName4, "g"); }); - ecdb.withPreparedStatement("INSERT INTO Test.B (h.a, h.b, i) VALUES ('h.a' ,'h.b', 'i')", (stmt: ECSqlStatement) => { + ecdb.withCachedWriteStatement("INSERT INTO Test.B (h.a, h.b, i) VALUES ('h.a' ,'h.b', 'i')", (stmt: ECSqlWriteStatement) => { const res: ECSqlInsertResult = stmt.stepForInsert(); assert.equal(res.status, DbResult.BE_SQLITE_DONE); assert.isDefined(res.id); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated ecdb.withPreparedStatement("SELECT h, i FROM Test.B", (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); // getRow just returns the enum values @@ -3097,8 +3192,9 @@ describe("ECSqlStatement", () => { assert.equal(row.h.a, "h.a"); assert.equal(row.h.b, "h.b"); assert.equal(row.i, "i"); - + // eslint-disable-next-line @typescript-eslint/no-deprecated const val0: ECSqlValue = stmt.getValue(0); + // eslint-disable-next-line @typescript-eslint/no-deprecated const colInfo0: ECSqlColumnInfo = val0.columnInfo; assert.equal(colInfo0.getPropertyName(), "h"); @@ -3107,8 +3203,9 @@ describe("ECSqlStatement", () => { const originPropertyName0 = colInfo0.getOriginPropertyName(); assert.isDefined(originPropertyName0); assert.equal(originPropertyName0, "h"); - + // eslint-disable-next-line @typescript-eslint/no-deprecated const val1: ECSqlValue = stmt.getValue(1); + // eslint-disable-next-line @typescript-eslint/no-deprecated const colInfo1: ECSqlColumnInfo = val1.columnInfo; assert.equal(colInfo1.getPropertyName(), "i"); @@ -3157,10 +3254,10 @@ describe("ECSqlStatement", () => { const point3dValue = new Point3d(15, 30, 45); const structValue = { structClassProperty: "test string value for struct property" }; - let r = await ecdb.withPreparedStatement( + let r = await ecdb.withCachedWriteStatement( `INSERT INTO ts.Foo(booleanProperty, blobProperty, doubleProperty, customIdProperty, customIdSetProperty, intProperty, longProperty, stringProperty, nullProperty, point2dProperty, point3dProperty) VALUES(:booleanValue, :blobValue, :doubleValue, :customIdValue, :customIdSetValue, :intValue, :longValue, :stringValue, :nullValue, :point2dValue, :point3dValue)`, - async (stmt: ECSqlStatement) => { + async (stmt: ECSqlWriteStatement) => { stmt.bindBoolean("booleanValue", booleanValue); stmt.bindBlob("blobValue", blobValue); stmt.bindDouble("doubleValue", doubleValue); @@ -3215,9 +3312,9 @@ describe("ECSqlStatement", () => { assert.isFalse(await reader.step()); - r = await ecdb.withPreparedStatement( + r = await ecdb.withCachedWriteStatement( "INSERT INTO ts.Baz(structProperty) VALUES(:structValue)", - async (stmt: ECSqlStatement) => { + async (stmt: ECSqlWriteStatement) => { stmt.bindStruct("structValue", structValue); return stmt.stepForInsert(); }, diff --git a/core/backend/src/test/ecdb/SqliteStatement.test.ts b/core/backend/src/test/ecdb/SqliteStatement.test.ts index 79314cf5da39..e1d63310b47c 100644 --- a/core/backend/src/test/ecdb/SqliteStatement.test.ts +++ b/core/backend/src/test/ecdb/SqliteStatement.test.ts @@ -429,12 +429,14 @@ describe("SqliteStatement", () => { // expect log message when statement fails slm = new SequentialLogMatcher(); slm.append().error().category("ECDb").message("ECClass 'abc.def' does not exist or could not be loaded."); + // eslint-disable-next-line @typescript-eslint/no-deprecated assert.throw(() => ecdb.withPreparedStatement("SELECT abc FROM abc.def", () => { }), "ECClass 'abc.def' does not exist or could not be loaded."); assert.isTrue(slm.finishAndDispose(), "logMatcher should detect log"); // now pass suppress log error which mean we should not get the error slm = new SequentialLogMatcher(); slm.append().error().category("ECDb").message("ECClass 'abc.def' does not exist or could not be loaded."); + // eslint-disable-next-line @typescript-eslint/no-deprecated assert.throw(() => ecdb.withPreparedStatement("SELECT abc FROM abc.def", () => { }, /* logErrors = */ false), ""); // BUG: we do not see error message assert.isFalse(slm.finishAndDispose(), "logMatcher should not detect log"); }); diff --git a/core/backend/src/test/ecsql/src/ECSqlTestGenerator.ts b/core/backend/src/test/ecsql/src/ECSqlTestGenerator.ts index 67a011bb4147..85a6c941a5a3 100644 --- a/core/backend/src/test/ecsql/src/ECSqlTestGenerator.ts +++ b/core/backend/src/test/ecsql/src/ECSqlTestGenerator.ts @@ -16,21 +16,22 @@ import { format } from "sql-formatter"; // Call like this: // node lib\cjs\test\ecsql\src\ECSqlTestGenerator.js AllProperties.bim "SELECT * FROM meta.ECSchemaDef LIMIT 2" -t // node lib\cjs\test\ecsql\src\ECSqlTestGenerator.js AllProperties.bim "SELECT te.ECInstanceId [MyId], te.s, te.DT [Date], row_number() over(PARTITION BY te.DT ORDER BY te.ECInstanceId) as [RowNumber] from aps.TestElement te WHERE te.i < 106" -t -async function runConcurrentQuery(imodel: IModelDb, sql: string): Promise<{metadata: any[], rows: any[] }> { +async function runConcurrentQuery(imodel: IModelDb, sql: string): Promise<{ metadata: any[], rows: any[] }> { const queryOptions: QueryOptionsBuilder = new QueryOptionsBuilder(); queryOptions.setRowFormat(QueryRowFormat.UseECSqlPropertyNames); const reader = imodel.createQueryReader(sql, undefined, queryOptions.getOptions()); const rows = await reader.toArray(); const metadata = await reader.getMetaData(); - metadata.forEach((value: QueryPropertyMetaData)=> delete (value as any).extendType); - return {metadata, rows }; + metadata.forEach((value: QueryPropertyMetaData) => delete (value as any).extendType); + return { metadata, rows }; } function pullAdditionalMetadataThroughECSqlStatement(imodel: IModelDb, metadata: any[], sql: string): void { + // eslint-disable-next-line @typescript-eslint/no-deprecated using stmt = imodel.prepareStatement(sql); if (stmt.step() === DbResult.BE_SQLITE_ROW) { const colCount = stmt.getColumnCount(); - if(colCount !== metadata.length) { + if (colCount !== metadata.length) { // eslint-disable-next-line no-console console.error(`Column count mismatch: ${colCount} != ${metadata.length}. Not generating metadata from statement.`); return; @@ -76,7 +77,7 @@ function generateHash(input: string): string { function writeMarkdownFile(dataset: string, sql: string, columns: any[], results: any[], useTables: boolean): void { const hash = generateHash(sql); if (sql.length > 100) { // we format the SQL if it's too long - sql = format(sql, {language: "sqlite", keywordCase: "upper", "tabWidth": 2, indentStyle: "standard", logicalOperatorNewline: "after"}); + sql = format(sql, { language: "sqlite", keywordCase: "upper", "tabWidth": 2, indentStyle: "standard", logicalOperatorNewline: "after" }); } let markdownContent = `# GeneratedTest #${dataset} - ${hash} @@ -98,7 +99,7 @@ ${arrayToMarkdownTable(results)} } else { markdownContent += ` \`\`\`json -${JSON.stringify({columns}, null, 2)} +${JSON.stringify({ columns }, null, 2)} \`\`\` \`\`\`json diff --git a/core/backend/src/test/ecsql/src/ECSqlTestRunner.test.ts b/core/backend/src/test/ecsql/src/ECSqlTestRunner.test.ts index ca13d33bb783..cefdfdf99bd0 100644 --- a/core/backend/src/test/ecsql/src/ECSqlTestRunner.test.ts +++ b/core/backend/src/test/ecsql/src/ECSqlTestRunner.test.ts @@ -31,30 +31,30 @@ describe("Markdown based ECDb test runner", async () => { after(() => { for (const key in snapshotDbs) { - if (snapshotDbs.hasOwnProperty(key)) { - (snapshotDbs[key as keyof typeof snapshotDbs])?.close(); - } + if (snapshotDbs.hasOwnProperty(key)) { + (snapshotDbs[key as keyof typeof snapshotDbs])?.close(); + } } }); const tests: ECDbTestProps[] = ECDbMarkdownTestParser.parse(); //TODO: Mechanism to run a single test, put something like it.only into the test md which causes this loop to only run those tests for (const test of tests) { - if(!test.dataset) { + if (!test.dataset) { logWarning(`Skipping test ${test.title} because it does not have a dataset`); continue; } - if(test.dataset.toLowerCase() !== TestDataset.AllProperties.toLowerCase()) { + if (test.dataset.toLowerCase() !== TestDataset.AllProperties.toLowerCase()) { logWarning(`Skipping test ${test.title} because dataset ${test.dataset} is not recognized`); continue; } const dataset = TestDataset.AllProperties; - if(test.mode === ECDbTestMode.Both || test.mode === ECDbTestMode.Statement) { - if(test.skip) + if (test.mode === ECDbTestMode.Both || test.mode === ECDbTestMode.Statement) { + if (test.skip) it(`${test.fileName}: ${test.title} (Statement) skipped. Reason: ${test.skip}`); - else if(test.only) + else if (test.only) it.only(`${test.fileName}: ${test.title} (Statement)`, () => { runECSqlStatementTest(test, dataset); }); @@ -62,12 +62,12 @@ describe("Markdown based ECDb test runner", async () => { it(`${test.fileName}: ${test.title} (Statement)`, () => { runECSqlStatementTest(test, dataset); }); - } + } - if(test.mode === ECDbTestMode.Both || test.mode === ECDbTestMode.ConcurrentQuery) { - if(test.skip) + if (test.mode === ECDbTestMode.Both || test.mode === ECDbTestMode.ConcurrentQuery) { + if (test.skip) it(`${test.fileName}: ${test.title} (ConcurrentQuery) skipped. Reason: ${test.skip}`); - else if(test.only) + else if (test.only) it.only(`${test.fileName}: ${test.title} (ConcurrentQuery)`, async () => { await runConcurrentQueryTest(test, dataset); }); @@ -84,6 +84,7 @@ function runECSqlStatementTest(test: ECDbTestProps, dataset: TestDataset) { if (!imodel) { assert.fail(`Dataset ${dataset} is not loaded`); } + // eslint-disable-next-line @typescript-eslint/no-deprecated let stmt: ECSqlStatement | undefined; if (test.sql === undefined) { assert.fail("Test does not have an ECSql statement"); @@ -91,26 +92,27 @@ function runECSqlStatementTest(test: ECDbTestProps, dataset: TestDataset) { try { // TODO: statement options should be exposed through the markdown + // eslint-disable-next-line @typescript-eslint/no-deprecated stmt = imodel.prepareStatement(test.sql); // TODO: Wire up logic for tests we expect to fail during prepare } catch (error: any) { - if(test.errorDuringPrepare) + if (test.errorDuringPrepare) return; else assert.fail(`Error during prepare of Statement: ${error.message}`); } - if(test.errorDuringPrepare) + if (test.errorDuringPrepare) assert.fail(`Statement is expected to fail during prepare`); try { - if(test.binders !== undefined) { + if (test.binders !== undefined) { for (const binder of test.binders) { // eslint-disable-next-line radix let id: number | string = Number.parseInt(binder.indexOrName); if (isNaN(id)) id = binder.indexOrName; - switch(binder.type.toLowerCase()) { // TODO: replace props variables in binder.value + switch (binder.type.toLowerCase()) { // TODO: replace props variables in binder.value case "null": stmt.bindNull(id); break; @@ -128,9 +130,9 @@ function runECSqlStatementTest(test: ECDbTestProps, dataset: TestDataset) { stmt.bindId(id, binder.value); break; case "idset": - const values: string[] = binder.value.slice(1,-1).split(","); - const trimmedValues = values.map((value:string)=> - value.trim() + const values: string[] = binder.value.slice(1, -1).split(","); + const trimmedValues = values.map((value: string) => + value.trim() ); stmt.bindIdSet(id, trimmedValues); break; @@ -139,15 +141,15 @@ function runECSqlStatementTest(test: ECDbTestProps, dataset: TestDataset) { break; case "point2d": const parsedVal2d = JSON.parse(binder.value); - stmt.bindPoint2d(id, {x: parsedVal2d.X, y: parsedVal2d.Y}); + stmt.bindPoint2d(id, { x: parsedVal2d.X, y: parsedVal2d.Y }); break; case "point3d": const parsedVal3d = JSON.parse(binder.value); - stmt.bindPoint3d(id, {x: parsedVal3d.X, y: parsedVal3d.Y, z: parsedVal3d.Z}); + stmt.bindPoint3d(id, { x: parsedVal3d.X, y: parsedVal3d.Y, z: parsedVal3d.Z }); break; case "blob": - const arrayValues: string[] = binder.value.slice(1,-1).split(","); - const numbers = arrayValues.map((value:string)=> + const arrayValues: string[] = binder.value.slice(1, -1).split(","); + const numbers = arrayValues.map((value: string) => // eslint-disable-next-line radix parseInt(value.trim()) ); @@ -199,7 +201,7 @@ function runECSqlStatementTest(test: ECDbTestProps, dataset: TestDataset) { expectedResult = buildBinaryData(expectedResult); const rowArgs: ECSqlRowArg = { rowFormat: getRowFormat(test.rowFormat), classIdsToClassNames: test.convertClassIdsToClassNames }; // TODO: abbreviate blobs is not supported here? - if(test.abbreviateBlobs) + if (test.abbreviateBlobs) logWarning("Abbreviate blobs is not supported for statement tests"); const actualResult = stmt.getRow(rowArgs); @@ -219,13 +221,13 @@ function runECSqlStatementTest(test: ECDbTestProps, dataset: TestDataset) { assert.fail(`Expected ${test.expectedResults.length} rows but got ${resultCount}`); } } finally { - if(stmt !== undefined) + if (stmt !== undefined) stmt[Symbol.dispose](); } } -function getRowFormat(rowFormat: ECDbTestRowFormat) : QueryRowFormat { - switch(rowFormat) { +function getRowFormat(rowFormat: ECDbTestRowFormat): QueryRowFormat { + switch (rowFormat) { case ECDbTestRowFormat.ECSqlNames: return QueryRowFormat.UseECSqlPropertyNames; case ECDbTestRowFormat.ECSqlIndexes: @@ -248,7 +250,7 @@ async function runConcurrentQueryTest(test: ECDbTestProps, dataset: TestDataset) assert.fail("Test does not have an ECSql statement"); } let params: QueryBinder | undefined; - if(test.binders !== undefined) { + if (test.binders !== undefined) { params = new QueryBinder(); for (const binder of test.binders) { // eslint-disable-next-line radix @@ -256,7 +258,7 @@ async function runConcurrentQueryTest(test: ECDbTestProps, dataset: TestDataset) if (isNaN(id)) id = binder.indexOrName; - switch(binder.type.toLowerCase()) { // TODO: replace props variables in binder.value + switch (binder.type.toLowerCase()) { // TODO: replace props variables in binder.value case "null": params.bindNull(id); break; @@ -278,9 +280,9 @@ async function runConcurrentQueryTest(test: ECDbTestProps, dataset: TestDataset) params.bindId(id, binder.value); break; case "idset": - const values: string[] = binder.value.slice(1,-1).split(","); - const trimmedValues = values.map((value:string)=> - value.trim() + const values: string[] = binder.value.slice(1, -1).split(","); + const trimmedValues = values.map((value: string) => + value.trim() ); params.bindIdSet(id, trimmedValues); break; @@ -293,10 +295,10 @@ async function runConcurrentQueryTest(test: ECDbTestProps, dataset: TestDataset) params.bindPoint3d(id, new Point3d(parsedVal3d.X, parsedVal3d.Y, parsedVal3d.Z)); break; case "blob": - const arrayValues: string[] = binder.value.slice(1,-1).split(","); - const numbers = arrayValues.map((value:string)=> - // eslint-disable-next-line radix - parseInt(value.trim()) + const arrayValues: string[] = binder.value.slice(1, -1).split(","); + const numbers = arrayValues.map((value: string) => + // eslint-disable-next-line radix + parseInt(value.trim()) ); params.bindBlob(id, Uint8Array.of(...numbers)); break; @@ -313,28 +315,29 @@ async function runConcurrentQueryTest(test: ECDbTestProps, dataset: TestDataset) queryOptions.rowFormat = getRowFormat(test.rowFormat); if (test.abbreviateBlobs) queryOptions.abbreviateBlobs = true; - if (test.convertClassIdsToClassNames) + if (test.convertClassIdsToClassNames) { + // eslint-disable-next-line @typescript-eslint/no-deprecated queryOptions.convertClassIdsToClassNames = true; - + } try { reader = imodel.createQueryReader(test.sql, params, queryOptions); // TODO: Wire up logic for tests we expect to fail during prepare } catch (error: any) { - assert.fail(`Error during creating QueryReader: ${error.message}`); + assert.fail(`Error during creating QueryReader: ${error.message}`); } let resultCount = 0; let rows; - try{ + try { rows = await reader.toArray(); } catch (error: any) { - if(test.errorDuringPrepare) + if (test.errorDuringPrepare) return; else assert.fail(`Error during prepare of Concurrent Query: ${error.message}`); } - if(test.errorDuringPrepare) + if (test.errorDuringPrepare) assert.fail(`Statement is expected to fail during prepare`); const colMetaData = await reader.getMetaData(); @@ -353,7 +356,7 @@ async function runConcurrentQueryTest(test: ECDbTestProps, dataset: TestDataset) assert.strictEqual(colInfo.accessString, expectedColInfo.accessString, `Expected access string ${expectedColInfo.accessString} but got ${colInfo.accessString} for column index ${i}`); if (expectedColInfo.typeName !== undefined) assert.strictEqual(colInfo.typeName, expectedColInfo.typeName, `Expected type name ${expectedColInfo.typeName} but got ${colInfo.typeName} for column index ${i}`); - if(expectedColInfo.className !== undefined) + if (expectedColInfo.className !== undefined) assert.strictEqual(colInfo.className, expectedColInfo.className, `Expected class name ${expectedColInfo.className} but got ${colInfo.className} for column index ${i}`); assert.strictEqual(colInfo.extendedType, expectedColInfo.extendedType, `Expected extended type ${expectedColInfo.extendedType} but got ${colInfo.extendedType} for column index ${i}`); assert.strictEqual(colInfo.extendType, expectedColInfo.extendedType === undefined ? "" : expectedColInfo.extendedType, `Expected extend type ${expectedColInfo.extendedType === undefined ? "" : expectedColInfo.extendedType} but got ${colInfo.extendType} for column index ${i}`); // eslint-disable-line @typescript-eslint/no-deprecated @@ -381,23 +384,18 @@ async function runConcurrentQueryTest(test: ECDbTestProps, dataset: TestDataset) } } -function checkingExpectedResults(rowFormat : ECDbTestRowFormat, actualResult: any, expectedResult: any, indexesToInclude?: number[]) -{ - if(rowFormat === ECDbTestRowFormat.ECSqlIndexes && indexesToInclude) - { +function checkingExpectedResults(rowFormat: ECDbTestRowFormat, actualResult: any, expectedResult: any, indexesToInclude?: number[]) { + if (rowFormat === ECDbTestRowFormat.ECSqlIndexes && indexesToInclude) { let i: any = 0; - for(const key of Object.keys(expectedResult)) - { + for (const key of Object.keys(expectedResult)) { assert.deepEqual(actualResult[indexesToInclude[i]], expectedResult[key], `Expected ${JSON.stringify(expectedResult[key])} but got ${JSON.stringify(actualResult[indexesToInclude[i]])}`); i++; - } + } } - else - { - for(const key of Object.keys(expectedResult)) - { + else { + for (const key of Object.keys(expectedResult)) { assert.deepEqual(actualResult[key], expectedResult[key], `Expected ${JSON.stringify(expectedResult[key])} but got ${JSON.stringify(actualResult[key])}`); - } + } } } function logWarning(message: string) { diff --git a/core/backend/src/test/element/ElementRoundTrip.test.ts b/core/backend/src/test/element/ElementRoundTrip.test.ts index 4c9d88a5723d..b04a51212cb8 100644 --- a/core/backend/src/test/element/ElementRoundTrip.test.ts +++ b/core/backend/src/test/element/ElementRoundTrip.test.ts @@ -580,6 +580,7 @@ describe("Element and ElementAspect roundtrip test for all type of properties", assert.equal(rowCount, 1); // verify via ecsql statement + // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("SELECT * FROM ts.TestElement", async (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const stmtRow = stmt.getRow() as TestElement; @@ -587,6 +588,7 @@ describe("Element and ElementAspect roundtrip test for all type of properties", }); // Verify system properties via ecsql statement + // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("select ECInstanceId, ECClassId, Model from ts.TestElement", async (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); verifySystemProperty(stmt.getRow() as TestElement, expectedSystemProperty); @@ -626,6 +628,7 @@ describe("Element and ElementAspect roundtrip test for all type of properties", assert.equal(rowCount, 1); // verify via ecsql statement + // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("SELECT * FROM ts.TestElement", async (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const stmtRow = stmt.getRow() as TestElement; @@ -633,6 +636,7 @@ describe("Element and ElementAspect roundtrip test for all type of properties", }); // Verify system properties via ecsql statement + // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("select ECInstanceId, ECClassId, Model from ts.TestElement", async (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); verifySystemProperty(stmt.getRow() as TestElement, expectedSystemProperty); @@ -665,6 +669,7 @@ describe("Element and ElementAspect roundtrip test for all type of properties", assert.equal(rowCount, 1); // Verify via an ECSql statement + // eslint-disable-next-line @typescript-eslint/no-deprecated await iModel.withPreparedStatement("SELECT * FROM ts.TestElementAspect", async (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const stmtRow = stmt.getRow() as TestElementAspect; @@ -690,6 +695,7 @@ describe("Element and ElementAspect roundtrip test for all type of properties", assert.isFalse(await reader.step()); // Verify via an ECSql statement + // eslint-disable-next-line @typescript-eslint/no-deprecated await iModel.withPreparedStatement("SELECT ECInstanceId, ECClassId, Element FROM ts.TestElementAspect", async (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); verifySystemProperty(stmt.getRow() as TestElementAspect, expectedSystemProperty); @@ -796,6 +802,7 @@ describe("Element and ElementAspect roundtrip test for all type of properties", assert.equal(rowCount, 1); // verify via ecsql statement + // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("SELECT * FROM ts.TestElementRefersToElements", async (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const stmtRow = stmt.getRow() as TestElementRefersToElements; @@ -826,6 +833,7 @@ describe("Element and ElementAspect roundtrip test for all type of properties", assert.isFalse(await reader.step()); // verify system properties via ecsql statement + // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("SELECT ECInstanceId, ECClassId, SourceECInstanceId, SourceECClassId, TargetECInstanceid, TargetECClassId FROM ts.TestElementRefersToElements", async (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); verifySystemProperty(stmt.getRow() as TestElementRefersToElements, expectedSystemProperties); @@ -857,6 +865,7 @@ describe("Element and ElementAspect roundtrip test for all type of properties", assert.equal(rowCount, 1); // verify via ecsql statement + // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("SELECT * FROM ts.TestElementRefersToElements", async (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const stmtRow = stmt.getRow() as TestElementRefersToElements; @@ -875,6 +884,7 @@ describe("Element and ElementAspect roundtrip test for all type of properties", assert.isFalse(await reader.step()); // verify system properties via ecsql statement + // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("SELECT ECInstanceId, ECClassId, SourceECInstanceId, SourceECClassId, TargetECInstanceid, TargetECClassId FROM ts.TestElementRefersToElements", async (stmt: ECSqlStatement) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); verifySystemProperty(stmt.getRow() as TestElementRefersToElements, expectedSystemProperties); diff --git a/core/backend/src/test/imodel/ElementTreeWalker.test.ts b/core/backend/src/test/imodel/ElementTreeWalker.test.ts index 1a3aa76168a8..0a6a0edbf12d 100644 --- a/core/backend/src/test/imodel/ElementTreeWalker.test.ts +++ b/core/backend/src/test/imodel/ElementTreeWalker.test.ts @@ -57,6 +57,7 @@ function doesModelExist(iModel: IModelDb, mid: Id64String): boolean { } function doesGroupRelationshipExist(iModel: IModelDb, source: Id64String, target: Id64String): boolean { + // eslint-disable-next-line @typescript-eslint/no-deprecated return iModel.withPreparedStatement(`select count(*) from ${ElementGroupsMembers.classFullName} where sourceecinstanceid=? and targetecinstanceid=?`, (stmt) => { stmt.bindId(1, source); stmt.bindId(2, target); diff --git a/core/backend/src/test/imodel/IModel.test.ts b/core/backend/src/test/imodel/IModel.test.ts index 0166ec527dac..811d32dd9433 100644 --- a/core/backend/src/test/imodel/IModel.test.ts +++ b/core/backend/src/test/imodel/IModel.test.ts @@ -756,6 +756,7 @@ describe("iModel", () => { it("should be some categories", () => { const categorySql = `SELECT ECInstanceId FROM ${Category.classFullName}`; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel1.withPreparedStatement(categorySql, (categoryStatement: ECSqlStatement): void => { let numCategories = 0; while (DbResult.BE_SQLITE_ROW === categoryStatement.step()) { @@ -776,6 +777,7 @@ describe("iModel", () => { // get the subcategories const subCategorySql = `SELECT ECInstanceId FROM ${SubCategory.classFullName} WHERE Parent.Id=:parentId`; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel1.withPreparedStatement(subCategorySql, (subCategoryStatement: ECSqlStatement): void => { let numSubCategories = 0; subCategoryStatement.bindId("parentId", categoryId); @@ -795,6 +797,7 @@ describe("iModel", () => { it("should be some 2d elements", () => { const sql = `SELECT ECInstanceId FROM ${DrawingGraphic.classFullName}`; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel2.withPreparedStatement(sql, (statement: ECSqlStatement): void => { let numDrawingGraphics = 0; let found25: boolean = false; @@ -891,6 +894,7 @@ describe("iModel", () => { it("should be children of RootSubject", () => { const sql = `SELECT ECInstanceId FROM ${Model.classFullName} WHERE ParentModel.Id=:parentModelId`; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel2.withPreparedStatement(sql, (statement: ECSqlStatement): void => { statement.bindId("parentModelId", IModel.repositoryModelId); let numModels = 0; @@ -1159,6 +1163,7 @@ describe("iModel", () => { it("should exercise ECSqlStatement (backend only)", () => { // Reject an invalid statement try { + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel2.prepareStatement("select no_such_property, codeValue from bis.element", false); assert.fail("prepare should have failed with an exception"); } catch (err: any) { @@ -1167,6 +1172,7 @@ describe("iModel", () => { } let lastId: string = ""; let firstCodeValue: string = ""; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel2.withPreparedStatement("select ecinstanceid, codeValue from bis.element", (stmt: ECSqlStatement) => { assert.isNotNull(stmt); // Reject an attempt to bind when there are no placeholders in the statement @@ -1215,6 +1221,7 @@ describe("iModel", () => { assert.equal(firstCodeValueIter, firstCodeValue, "iterator loop should find the first non-null code value as the step loop"); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel2.withPreparedStatement("select ecinstanceid, codeValue from bis.element WHERE (ecinstanceid=?)", (stmt3: ECSqlStatement) => { // Now try a statement with a placeholder const idToFind = Id64.fromJSON(lastId); @@ -1231,6 +1238,7 @@ describe("iModel", () => { }); let firstCodeValueId: Id64String | undefined; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel2.withPreparedStatement("select ecinstanceid, codeValue from bis.element WHERE (codeValue = :codevalue)", (stmt4: ECSqlStatement) => { // Try a named placeholder const codeValueToFind = firstCodeValue; @@ -1251,8 +1259,10 @@ describe("iModel", () => { const ids = imodel2.queryEntityIds({ from: "bis.element", where: "codevalue=:cv", bindings: { cv: firstCodeValue } }); assert.equal(ids.values().next().value, firstCodeValueId); + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel2.withPreparedStatement("select ecinstanceid as id, codevalue from bis.element", (stmt5: ECSqlStatement) => { while (DbResult.BE_SQLITE_ROW === stmt5.step()) { + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel2.withPreparedStatement("select codevalue from bis.element where ecinstanceid=?", (stmt6: ECSqlStatement) => { stmt6.bindId(1, stmt5.getRow().id); while (DbResult.BE_SQLITE_ROW === stmt6.step()) { @@ -1511,6 +1521,7 @@ describe("iModel", () => { DefinitionGroupGroupsDefinitions.insert(iModelDb, definitionGroupId, categoryId1); DefinitionGroupGroupsDefinitions.insert(iModelDb, definitionGroupId, categoryId2); DefinitionGroupGroupsDefinitions.insert(iModelDb, definitionGroupId, categoryId3); + // eslint-disable-next-line @typescript-eslint/no-deprecated const numMembers = iModelDb.withPreparedStatement(`SELECT COUNT(*) FROM ${DefinitionGroupGroupsDefinitions.classFullName}`, (statement: ECSqlStatement): number => { return statement.step() === DbResult.BE_SQLITE_ROW ? statement.getValue(0).getInteger() : 0; }); @@ -2558,8 +2569,11 @@ describe("iModel", () => { it("tryPrepareStatement", () => { const sql = `SELECT * FROM ${Element.classFullName} LIMIT 1`; const invalidSql = "SELECT * FROM InvalidSchemaName:InvalidClassName LIMIT 1"; + // eslint-disable-next-line @typescript-eslint/no-deprecated assert.throws(() => imodel1.prepareStatement(invalidSql, false)); + // eslint-disable-next-line @typescript-eslint/no-deprecated assert.isUndefined(imodel1.tryPrepareStatement(invalidSql)); + // eslint-disable-next-line @typescript-eslint/no-deprecated using statement: ECSqlStatement | undefined = imodel1.tryPrepareStatement(sql); assert.isDefined(statement); assert.isTrue(statement?.isPrepared); diff --git a/core/backend/src/test/schema/GenericDomain.test.ts b/core/backend/src/test/schema/GenericDomain.test.ts index 8092085c5e1d..3cf7ce864392 100644 --- a/core/backend/src/test/schema/GenericDomain.test.ts +++ b/core/backend/src/test/schema/GenericDomain.test.ts @@ -18,6 +18,7 @@ import { IModelTestUtils } from "../IModelTestUtils"; describe("Generic Domain", () => { function count(iModelDb: IModelDb, classFullName: string): number { + // eslint-disable-next-line @typescript-eslint/no-deprecated return iModelDb.withPreparedStatement(`SELECT COUNT(*) FROM ${classFullName}`, (statement: ECSqlStatement): number => { return DbResult.BE_SQLITE_ROW === statement.step() ? statement.getValue(0).getInteger() : 0; }); diff --git a/core/backend/src/test/standalone/IModelWrite.test.ts b/core/backend/src/test/standalone/IModelWrite.test.ts index 9de3498e0f80..faac73c34dcf 100644 --- a/core/backend/src/test/standalone/IModelWrite.test.ts +++ b/core/backend/src/test/standalone/IModelWrite.test.ts @@ -486,6 +486,7 @@ describe("IModelWriteTest", () => { const briefcaseDb = await BriefcaseDb.open({ fileName: briefcaseProps.fileName }); briefcaseDb.channels.addAllowedChannel(ChannelControl.sharedChannelName); let firstNonRootElement = { id: undefined, codeValue: "test" }; + // eslint-disable-next-line @typescript-eslint/no-deprecated briefcaseDb.withPreparedStatement("SELECT * from Bis.Element LIMIT 1 OFFSET 1", (stmt: ECSqlStatement) => { if (stmt.step() === DbResult.BE_SQLITE_ROW) { firstNonRootElement = stmt.getRow(); @@ -616,6 +617,7 @@ describe("IModelWriteTest", () => { assert.equal(changesets.length, 2); } let rows: any[] = []; + // eslint-disable-next-line @typescript-eslint/no-deprecated rwIModel.withPreparedStatement("SELECT * FROM TestDomain.Test2dElement", (stmt: ECSqlStatement) => { while (stmt.step() === DbResult.BE_SQLITE_ROW) { rows.push(stmt.getRow()); @@ -633,6 +635,7 @@ describe("IModelWriteTest", () => { // pull and merge changes await rwIModel2.pullChanges({ accessToken: userToken }); rows = []; + // eslint-disable-next-line @typescript-eslint/no-deprecated rwIModel2.withPreparedStatement("SELECT * FROM TestDomain.Test2dElement", (stmt: ECSqlStatement) => { while (stmt.step() === DbResult.BE_SQLITE_ROW) { rows.push(stmt.getRow()); @@ -722,6 +725,7 @@ describe("IModelWriteTest", () => { assert.equal(changesets.length, 5); } rows = []; + // eslint-disable-next-line @typescript-eslint/no-deprecated rwIModel.withPreparedStatement("SELECT * FROM TestDomain.Test2dElement", (stmt: ECSqlStatement) => { while (stmt.step() === DbResult.BE_SQLITE_ROW) { rows.push(stmt.getRow()); @@ -739,6 +743,7 @@ describe("IModelWriteTest", () => { assert.equal(rows.map((r) => r.v).filter((v) => v).length, 10); rows = []; + // eslint-disable-next-line @typescript-eslint/no-deprecated rwIModel.withPreparedStatement("SELECT * FROM TestDomain.Test2dElement2nd", (stmt: ECSqlStatement) => { while (stmt.step() === DbResult.BE_SQLITE_ROW) { rows.push(stmt.getRow()); @@ -760,6 +765,7 @@ describe("IModelWriteTest", () => { await rwIModel2.pullChanges({ accessToken: userToken }); rows = []; // Following fail without the fix in briefcase manager where we clear statement cache on schema changeset apply + // eslint-disable-next-line @typescript-eslint/no-deprecated rwIModel2.withPreparedStatement("SELECT * FROM TestDomain.Test2dElement", (stmt: ECSqlStatement) => { while (stmt.step() === DbResult.BE_SQLITE_ROW) { rows.push(stmt.getRow()); @@ -791,6 +797,7 @@ describe("IModelWriteTest", () => { } } rows = []; + // eslint-disable-next-line @typescript-eslint/no-deprecated rwIModel2.withPreparedStatement("SELECT * FROM TestDomain.Test2dElement2nd", (stmt: ECSqlStatement) => { while (stmt.step() === DbResult.BE_SQLITE_ROW) { rows.push(stmt.getRow()); diff --git a/core/backend/src/test/standalone/RenderMaterialElement.test.ts b/core/backend/src/test/standalone/RenderMaterialElement.test.ts index 0c2da791b925..5162cfdd4f0d 100644 --- a/core/backend/src/test/standalone/RenderMaterialElement.test.ts +++ b/core/backend/src/test/standalone/RenderMaterialElement.test.ts @@ -97,6 +97,7 @@ describe("RenderMaterialElement", () => { const db = SnapshotDb.openFile(seedFileName); let id: Id64String; + // eslint-disable-next-line @typescript-eslint/no-deprecated db.withStatement(`SELECT ECInstanceId from Bis.RenderMaterial`, (stmt) => { expect(stmt.step()).to.equal(DbResult.BE_SQLITE_ROW); id = stmt.getRow().id; diff --git a/core/common/src/ConcurrentQuery.ts b/core/common/src/ConcurrentQuery.ts index 741f90536397..3c69e3d69dd3 100644 --- a/core/common/src/ConcurrentQuery.ts +++ b/core/common/src/ConcurrentQuery.ts @@ -27,6 +27,7 @@ export enum QueryRowFormat { UseECSqlPropertyIndexes, /** Each row is an object in which each non-null column value can be accessed by a [remapped property name]($docs/learning/ECSqlRowFormat.md). * This format is backwards-compatible with the format produced by iTwin.js 2.x. Null values are omitted. + * @depreacted in 4.11. Switch to UseECSqlPropertyIndexes for best performance, and UseECSqlPropertyNames if you want a JSON object as the result. */ UseJsPropertyNames, } @@ -144,6 +145,7 @@ export interface QueryOptions extends BaseReaderOptions { /** * Convert ECClassId, SourceECClassId, TargetECClassId and RelClassId to respective name. * When true, XXXXClassId property will be returned as className. + * @deprecated in 4.11 Use ecsql function ec_classname to get class name instead. * */ convertClassIdsToClassNames?: boolean; /** @@ -223,8 +225,10 @@ export class QueryOptionsBuilder { * If set ECClassId, SourceECClassId and TargetECClassId system properties will return qualified name of class instead of a @typedef Id64String. * @param val A boolean value. * @returns @type QueryOptionsBuilder for fluent interface. + * @deprecated in 4.11 Use ecsql function ec_classname to get class name instead. */ public setConvertClassIdsToNames(val: boolean) { + // eslint-disable-next-line @typescript-eslint/no-deprecated this._options.convertClassIdsToClassNames = val; return this; } diff --git a/docs/changehistory/NextVersion.md b/docs/changehistory/NextVersion.md index 058e10d056ef..067c5bc85cb4 100644 --- a/docs/changehistory/NextVersion.md +++ b/docs/changehistory/NextVersion.md @@ -38,7 +38,7 @@ Table of contents: - [No pending/local changes](#no-pendinglocal-changes) - [With pending/local changes](#with-pendinglocal-changes) - [TypeScript configuration changes](#typescript-configuration-changes) - + - [Deprecated ECSqlStatement](#deprecated-ecsqlstatement) ## Selection set There are two similar selection-related concepts in `@itwin/core-frontend` - [SelectionSet]($core-frontend) and [HiliteSet]($core-frontend). The former is generally used by interactive tools (e.g. the "Move element" tool), so it contains what tools think is selected. The latter is used by the graphics system to know what elements to highlight, so it contains what users think is selected. Generally, we want the two sets to be in sync to avoid confusion why tools act on different elements than what users think are selected. Keeping them in sync was not always possible, because `HiliteSet` may store Model and SubCategory ids, but `SelectionSet` could only store Element ids. So we could end up in situations where a Model id is added to `HiliteSet` and `SelectionSet` is empty, making users think that all elements in that model are selected, but tools not knowing anything about it. @@ -392,3 +392,14 @@ class MyElement extends Element { ... } ``` +## Deprecated ECSqlStatement + +`ECSqlStatement` is deprecated in 4.11 Use [IModelDb.createQueryReader]($backend) or [ECDb.createQueryReader]($backend) + +Following are related classes to ECSqlStatement that are also mark depercate + * `ECEnumValue` + * `ECSqlValue` + * `ECSqlValueIterator` + * `ECSqlColumnInfo` + + In concurrent query `QueryOptions.convertClassIdsToClassNames` & `QueryOptionsBuilder.setConvertClassIdsToNames()` are deprecated. Use ECSQL ec_classname() function to convert class ids to class names. \ No newline at end of file diff --git a/domains/linear-referencing/backend/src/LinearReferencingElements.ts b/domains/linear-referencing/backend/src/LinearReferencingElements.ts index 1ad713a7d166..bc4a220252d9 100644 --- a/domains/linear-referencing/backend/src/LinearReferencingElements.ts +++ b/domains/linear-referencing/backend/src/LinearReferencingElements.ts @@ -561,13 +561,12 @@ export class LinearlyLocated { private static queryFirstLinearLocationAspectId(iModel: IModelDb, linearlyLocatedElementId: Id64String, className: string): Id64String | undefined { let aspectId: Id64String | undefined; - - iModel.withPreparedStatement(`SELECT ECInstanceId FROM LinearReferencing.${className} WHERE Element.Id=? LIMIT 1`, - (stmt: ECSqlStatement) => { - stmt.bindId(1, linearlyLocatedElementId); - if (stmt.step() === DbResult.BE_SQLITE_ROW) - aspectId = stmt.getValue(0).getId(); - }); + // eslint-disable-next-line @typescript-eslint/no-deprecated + iModel.withPreparedStatement(`SELECT ECInstanceId FROM LinearReferencing.${className} WHERE Element.Id=? LIMIT 1`, (stmt: ECSqlStatement) => { + stmt.bindId(1, linearlyLocatedElementId); + if (stmt.step() === DbResult.BE_SQLITE_ROW) + aspectId = stmt.getValue(0).getId(); + }); return aspectId; } @@ -662,16 +661,15 @@ export class LinearlyLocated { */ public static getLinearElementId(iModel: IModelDb, linearlyLocatedElementId: Id64String): Id64String | undefined { let linearElementId: Id64String | undefined; - iModel.withPreparedStatement( - "SELECT TargetECInstanceId FROM LinearReferencing.ILinearlyLocatedAlongILinearElement WHERE SourceECInstanceId = ?", - (stmt: ECSqlStatement) => { - stmt.bindId(1, linearlyLocatedElementId); - - if (DbResult.BE_SQLITE_ROW === stmt.step()) - linearElementId = stmt.getValue(0).getId(); - else - linearElementId = undefined; - }); + // eslint-disable-next-line @typescript-eslint/no-deprecated + iModel.withPreparedStatement("SELECT TargetECInstanceId FROM LinearReferencing.ILinearlyLocatedAlongILinearElement WHERE SourceECInstanceId = ?", (stmt: ECSqlStatement) => { + stmt.bindId(1, linearlyLocatedElementId); + + if (DbResult.BE_SQLITE_ROW === stmt.step()) + linearElementId = stmt.getValue(0).getId(); + else + linearElementId = undefined; + }); return linearElementId; } @@ -765,6 +763,7 @@ export class LinearElement { const ecsqlAndBindVals = ecSqlGen.generate(linearElementId); const linearLocationRefs: LinearLocationReference[] = []; + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement(ecsqlAndBindVals[0], (stmt: ECSqlStatement) => { stmt.bindValues(ecsqlAndBindVals[1]); diff --git a/domains/physical-material/backend/src/test/PhysicalMaterialSchema.test.ts b/domains/physical-material/backend/src/test/PhysicalMaterialSchema.test.ts index b9e5a426c5ea..2546d60d84a1 100644 --- a/domains/physical-material/backend/src/test/PhysicalMaterialSchema.test.ts +++ b/domains/physical-material/backend/src/test/PhysicalMaterialSchema.test.ts @@ -22,6 +22,7 @@ describe("PhysicalMaterialSchema", () => { }); function count(iModelDb: IModelDb, classFullName: string): number { + // eslint-disable-next-line @typescript-eslint/no-deprecated return iModelDb.withPreparedStatement(`SELECT COUNT(*) FROM ${classFullName}`, (statement: ECSqlStatement): number => { return DbResult.BE_SQLITE_ROW === statement.step() ? statement.getValue(0).getInteger() : 0; }); diff --git a/example-code/app/src/backend/RobotWorldEngine.ts b/example-code/app/src/backend/RobotWorldEngine.ts index 3d5e22aa1975..8c5f7849670a 100644 --- a/example-code/app/src/backend/RobotWorldEngine.ts +++ b/example-code/app/src/backend/RobotWorldEngine.ts @@ -36,6 +36,7 @@ export class RobotWorldEngine { } public static countRobots(iModelDb: IModelDb): number { + // eslint-disable-next-line @typescript-eslint/no-deprecated return iModelDb.withPreparedStatement(`SELECT COUNT(*) from ${RobotWorld.Class.Robot}`, (stmt: ECSqlStatement): number => { if (stmt.step() !== DbResult.BE_SQLITE_ROW) return 0; @@ -50,6 +51,7 @@ export class RobotWorldEngine { const selStmt = `SELECT rt.ECInstanceId FROM BisCore.SpatialIndex rt WHERE rt.ECInstanceId MATCH iModel_spatial_overlap_aabb(:bbox) AND rt.ECInstanceId <> :thisRobot`; + // eslint-disable-next-line @typescript-eslint/no-deprecated return iModelDb.withPreparedStatement(selStmt, (stmt: ECSqlStatement) => { stmt.bindRange3d("bbox", robot1.placement.calculateRange()); stmt.bindId("thisRobot", rid); @@ -69,6 +71,7 @@ export class RobotWorldEngine { const selStmt = `SELECT rt.ECInstanceId FROM BisCore.SpatialIndex rt WHERE rt.ECInstanceId MATCH iModel_spatial_overlap_aabb(:bbox) AND rt.ECInstanceId <> :thisRobot`; + // eslint-disable-next-line @typescript-eslint/no-deprecated return iModelDb.withPreparedStatement(selStmt, (stmt: ECSqlStatement) => { stmt.bindRange3d("bbox", robot1.placement.calculateRange()); stmt.bindId("thisRobot", rid); diff --git a/example-code/snippets/src/backend/DumpIModel.test.ts b/example-code/snippets/src/backend/DumpIModel.test.ts index 4393265363a9..9df148be6449 100644 --- a/example-code/snippets/src/backend/DumpIModel.test.ts +++ b/example-code/snippets/src/backend/DumpIModel.test.ts @@ -28,6 +28,7 @@ class DumpIModel { } // Iterate each Model const sql = `SELECT ECInstanceId AS id FROM ${Model.classFullName}`; + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement(sql, (statement: ECSqlStatement) => { while (DbResult.BE_SQLITE_ROW === statement.step()) { const row = statement.getRow(); @@ -42,6 +43,7 @@ class DumpIModel { fs.writeFileSync(outputFile, "["); // ECSQL to SELECT every Element in the specified Model const sql = `SELECT ECInstanceId AS id FROM ${Element.classFullName} WHERE Model.Id=:modelId`; + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement(sql, (statement: ECSqlStatement) => { statement.bindId("modelId", modelId); let isFirstEntry = true; diff --git a/example-code/snippets/src/backend/ECSQL-queries.test.ts b/example-code/snippets/src/backend/ECSQL-queries.test.ts index ab8ae49f2083..146bac988827 100644 --- a/example-code/snippets/src/backend/ECSQL-queries.test.ts +++ b/example-code/snippets/src/backend/ECSQL-queries.test.ts @@ -30,6 +30,7 @@ describe("Useful ECSQL queries", () => { // You could write the following query to find it. This query specifies that the // element you want is a PhysicalPartition, it has a code value of "Physical", // and it is a child of a Subject named "Subject1". + // eslint-disable-next-line @typescript-eslint/no-deprecated const partitionIds: Id64Set = iModel.withPreparedStatement(` select [partition].ecinstanceid @@ -37,15 +38,16 @@ describe("Useful ECSQL queries", () => { ${PhysicalPartition.classFullName} as [partition], (select ecinstanceid from ${Subject.classFullName} where CodeValue=:parentName) as parent where - [partition].codevalue=:partitionName and [partition].parent.id = parent.ecinstanceid; - `, (stmt: ECSqlStatement) => { - stmt.bindValue("parentName", "Subject1"); - stmt.bindValue("partitionName", "Physical"); - const ids: Id64Set = new Set(); - while (stmt.step() === DbResult.BE_SQLITE_ROW) - ids.add(stmt.getValue(0).getId()); - return ids; - }); + [partition].codevalue=:partitionName and [partition].parent.id = parent.ecinstanceid;`, + // eslint-disable-next-line @typescript-eslint/no-deprecated + (stmt: ECSqlStatement) => { + stmt.bindValue("parentName", "Subject1"); + stmt.bindValue("partitionName", "Physical"); + const ids: Id64Set = new Set(); + while (stmt.step() === DbResult.BE_SQLITE_ROW) + ids.add(stmt.getValue(0).getId()); + return ids; + }); assert.isNotEmpty(partitionIds); assert.equal(partitionIds.size, 1); @@ -69,6 +71,7 @@ describe("Useful ECSQL queries", () => { it("should select all elements in a model", () => { // __PUBLISH_EXTRACT_START__ ECSQL-backend-queries.select-elements-in-model const modelId: Id64String = IModelDb.repositoryModelId; + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement(`SELECT ECInstanceId AS id FROM ${Element.classFullName} WHERE Model.Id=:modelId`, (statement: ECSqlStatement) => { statement.bindId("modelId", modelId); while (DbResult.BE_SQLITE_ROW === statement.step()) { @@ -81,6 +84,7 @@ describe("Useful ECSQL queries", () => { it("should select all top-level elements in a model", () => { // __PUBLISH_EXTRACT_START__ ECSQL-backend-queries.select-top-level-elements-in-model const modelId: Id64String = IModelDb.repositoryModelId; + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement(`SELECT ECInstanceId AS id FROM ${Element.classFullName} WHERE Model.Id=:modelId AND Parent.Id IS NULL`, (statement: ECSqlStatement) => { statement.bindId("modelId", modelId); while (DbResult.BE_SQLITE_ROW === statement.step()) { @@ -93,6 +97,7 @@ describe("Useful ECSQL queries", () => { it("should select all child elements of the specified element", () => { // __PUBLISH_EXTRACT_START__ ECSQL-backend-queries.select-child-elements const parentId: Id64String = IModelDb.rootSubjectId; + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement(`SELECT ECInstanceId AS id FROM ${Element.classFullName} WHERE Parent.Id=:parentId`, (statement: ECSqlStatement) => { statement.bindId("parentId", parentId); while (DbResult.BE_SQLITE_ROW === statement.step()) { diff --git a/example-code/snippets/src/backend/ECSQL-spatial-queries.test.ts b/example-code/snippets/src/backend/ECSQL-spatial-queries.test.ts index 66d862fc275e..21104bbc596e 100644 --- a/example-code/snippets/src/backend/ECSQL-spatial-queries.test.ts +++ b/example-code/snippets/src/backend/ECSQL-spatial-queries.test.ts @@ -32,7 +32,9 @@ describe("Useful ECSQL spatial queries", () => { // __PUBLISH_EXTRACT_START__ EcsqlGeometryFunctions.iModel_bbox_areaxy // Compute the largest element area in the X-Y plane. let maxArea: number = 0; + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement(`SELECT iModel_bbox_areaxy(iModel_bbox(BBoxLow.X,BBoxLow.Y,BBoxLow.Z,BBoxHigh.X,BBoxHigh.Y,BBoxHigh.Z)) FROM ${GeometricElement3d.classFullName}`, + // eslint-disable-next-line @typescript-eslint/no-deprecated (stmt: ECSqlStatement) => { while (stmt.step() === DbResult.BE_SQLITE_ROW) { const thisArea: number = stmt.getValue(0).getDouble(); @@ -45,7 +47,9 @@ describe("Useful ECSQL spatial queries", () => { // Use the standard SUM operator to accumulate the results of the iModel_bbox_areaxy function. This shows that // ECSQL treats the built-in geometry functions as normal expressions. + // eslint-disable-next-line @typescript-eslint/no-deprecated const areaSum: number = iModel.withPreparedStatement(`SELECT SUM(iModel_bbox_areaxy(iModel_bbox(BBoxLow.X,BBoxLow.Y,BBoxLow.Z,BBoxHigh.X,BBoxHigh.Y,BBoxHigh.Z))) FROM ${GeometricElement3d.classFullName}`, + // eslint-disable-next-line @typescript-eslint/no-deprecated (stmt: ECSqlStatement) => { if (stmt.step() !== DbResult.BE_SQLITE_ROW) return 0; // ? @@ -73,7 +77,9 @@ describe("Useful ECSQL spatial queries", () => { WHERE e.model.id=? AND e.ecinstanceid=g.ecinstanceid `; + // eslint-disable-next-line @typescript-eslint/no-deprecated const rangeSum: Range3dProps = iModel.withPreparedStatement(bboxUnionStmtECSQL, + // eslint-disable-next-line @typescript-eslint/no-deprecated (stmt: ECSqlStatement) => { stmt.bindId(1, modelId); if (stmt.step() !== DbResult.BE_SQLITE_ROW) @@ -87,7 +93,9 @@ describe("Useful ECSQL spatial queries", () => { // This is an example of passing the WRONG TYPE of object to iModel_bbox_areaxy and getting an error. // This statement is wrong, because iModel_placement_angles returns a iModel_angles object, while iModel_bbox_areaxy expects a DGN_bbox object. // Note that the error is detected when you try to step the statement, not when you prepare it. + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement(`SELECT iModel_bbox_areaxy(iModel_angles(Yaw,Pitch,Roll)) FROM ${GeometricElement3d.classFullName}`, + // eslint-disable-next-line @typescript-eslint/no-deprecated (stmt: ECSqlStatement) => { // TODO: I expect an exception here: while (stmt.step() === DbResult.BE_SQLITE_ROW) { diff --git a/example-code/snippets/src/backend/ExecutingECSQL.ts b/example-code/snippets/src/backend/ExecutingECSQL.ts index 8f076f64f90f..d1a8dcd625b4 100644 --- a/example-code/snippets/src/backend/ExecutingECSQL.ts +++ b/example-code/snippets/src/backend/ExecutingECSQL.ts @@ -10,6 +10,7 @@ import { NavigationValue } from "@itwin/core-common"; function executeECSql_Binding(iModel: IModelDb) { // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_ByParameter_Positional + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE CodeValue=? AND LastMod>=?", (stmt: ECSqlStatement) => { stmt.bindString(1, "MyCode"); stmt.bindDateTime(2, "2018-01-01T12:00:00"); @@ -21,6 +22,7 @@ function executeECSql_Binding(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_ByParameter_Named + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE CodeValue=:code AND LastMod>=:lastmod", (stmt: ECSqlStatement) => { stmt.bindString("code", "MyCode"); stmt.bindDateTime("lastmod", "2018-01-01T12:00:00Z"); @@ -32,28 +34,29 @@ function executeECSql_Binding(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_BindValues_Positional - iModel.withPreparedStatement("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE CodeValue=? AND LastMod>=?", - (stmt: ECSqlStatement) => { - stmt.bindValues(["MyCode", "2018-01-01T12:00:00Z"]); + // eslint-disable-next-line @typescript-eslint/no-deprecated + iModel.withPreparedStatement("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE CodeValue=? AND LastMod>=?", (stmt: ECSqlStatement) => { + stmt.bindValues(["MyCode", "2018-01-01T12:00:00Z"]); - while (stmt.step() === DbResult.BE_SQLITE_ROW) { - // do something with the query result - } - }); + while (stmt.step() === DbResult.BE_SQLITE_ROW) { + // do something with the query result + } + }); // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_BindValues_Named - iModel.withPreparedStatement("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE CodeValue=:code AND LastMod>=:lastmod", - (stmt: ECSqlStatement) => { - stmt.bindValues({ code: "MyCode", lastmod: "2018-01-01T12:00:00Z" }); + // eslint-disable-next-line @typescript-eslint/no-deprecated + iModel.withPreparedStatement("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE CodeValue=:code AND LastMod>=:lastmod", (stmt: ECSqlStatement) => { + stmt.bindValues({ code: "MyCode", lastmod: "2018-01-01T12:00:00Z" }); - while (stmt.step() === DbResult.BE_SQLITE_ROW) { - // do something with the query result - } - }); + while (stmt.step() === DbResult.BE_SQLITE_ROW) { + // do something with the query result + } + }); // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_Navigation_ByParameter + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId FROM bis.Element WHERE Parent=?", (stmt: ECSqlStatement) => { stmt.bindNavigation(1, { id: "0x132" }); // ... @@ -61,6 +64,7 @@ function executeECSql_Binding(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_BindValues_Navigation + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId FROM bis.Element WHERE Parent=?", (stmt: ECSqlStatement) => { stmt.bindValues([{ id: "0x132" }]); // ... @@ -68,6 +72,7 @@ function executeECSql_Binding(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_NavigationId_ByParameter + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId FROM bis.Element WHERE Parent.Id=?", (stmt: ECSqlStatement) => { stmt.bindId(1, "0x132"); // ... @@ -75,6 +80,7 @@ function executeECSql_Binding(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_BindValues_NavigationId + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId FROM bis.Element WHERE Parent.Id=?", (stmt: ECSqlStatement) => { stmt.bindValues(["0x132"]); // ... @@ -82,6 +88,7 @@ function executeECSql_Binding(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_Struct_ByParameter + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT Name FROM myschema.Company WHERE Location=?", (stmt: ECSqlStatement) => { stmt.bindStruct(1, { street: "7123 Main Street", zip: 30211 }); // ... @@ -89,6 +96,7 @@ function executeECSql_Binding(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_BindValues_Struct + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT Name FROM myschema.Company WHERE Location=?", (stmt: ECSqlStatement) => { stmt.bindValues([{ street: "7123 Main Street", zip: 30211 }]); // ... @@ -96,6 +104,7 @@ function executeECSql_Binding(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_StructMembers_ByParameter + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT Name FROM myschema.Company WHERE Location.Street=? AND Location.Zip=?", (stmt: ECSqlStatement) => { stmt.bindString(1, "7123 Main Street"); stmt.bindInteger(2, 32443); @@ -104,6 +113,7 @@ function executeECSql_Binding(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_BindValues_StructMembers + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT Name FROM myschema.Company WHERE Location.Street=? AND Location.Zip=?", (stmt: ECSqlStatement) => { stmt.bindValues(["7123 Main Street", 32443]); // ... @@ -111,6 +121,7 @@ function executeECSql_Binding(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_Array_ByParameter + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT Name FROM myschema.Company WHERE PhoneNumbers=?", (stmt: ECSqlStatement) => { stmt.bindArray(1, ["+16134584201", "+16134584202", "+16134584222"]); // ... @@ -118,6 +129,7 @@ function executeECSql_Binding(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_BindValues_Array + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT Name FROM myschema.Company WHERE PhoneNumbers=?", (stmt: ECSqlStatement) => { stmt.bindValues([["+16134584201", "+16134584202", "+16134584222"]]); // ... @@ -127,6 +139,7 @@ function executeECSql_Binding(iModel: IModelDb) { function executeECSql_QueryResult(iModel: IModelDb) { // __PUBLISH_EXTRACT_START__ ExecuteECSql_GetRow_IllustrateRowFormat + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE Model.Id=?", (stmt: ECSqlStatement) => { stmt.bindId(1, "0x113"); @@ -139,6 +152,7 @@ function executeECSql_QueryResult(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_GetRow + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE Model.Id=?", (stmt: ECSqlStatement) => { stmt.bindId(1, "0x113"); @@ -158,15 +172,20 @@ function executeECSql_QueryResult(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_GetValue + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE Model.Id=?", (stmt: ECSqlStatement) => { stmt.bindId(1, "0x113"); console.log("ECInstanceId | ClassName | Parent Id | Parent RelClassName | LastMod"); while (stmt.step() === DbResult.BE_SQLITE_ROW) { + // eslint-disable-next-line @typescript-eslint/no-deprecated const idValue: ECSqlValue = stmt.getValue(0); + // eslint-disable-next-line @typescript-eslint/no-deprecated const classIdValue: ECSqlValue = stmt.getValue(1); + // eslint-disable-next-line @typescript-eslint/no-deprecated const parentValue: ECSqlValue = stmt.getValue(2); + // eslint-disable-next-line @typescript-eslint/no-deprecated const lastModValue: ECSqlValue = stmt.getValue(3); const id: Id64String = idValue.getId(); @@ -180,12 +199,15 @@ function executeECSql_QueryResult(iModel: IModelDb) { // __PUBLISH_EXTRACT_END__ // __PUBLISH_EXTRACT_START__ ExecuteECSql_GetValue_PreserveClassIds + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECClassId,Parent.RelECClassId FROM bis.Element WHERE Model.Id=?", (stmt: ECSqlStatement) => { stmt.bindId(1, "0x113"); console.log("ECClassId | Parent RelECClassId"); while (stmt.step() === DbResult.BE_SQLITE_ROW) { + // eslint-disable-next-line @typescript-eslint/no-deprecated const classIdValue: ECSqlValue = stmt.getValue(0); + // eslint-disable-next-line @typescript-eslint/no-deprecated const parentRelClassIdValue: ECSqlValue = stmt.getValue(1); const classId: string = classIdValue.getId(); diff --git a/full-stack-tests/backend/src/integration/ChangeSummary.test.ts b/full-stack-tests/backend/src/integration/ChangeSummary.test.ts index 9b7458d91cf9..85dda17f872d 100644 --- a/full-stack-tests/backend/src/integration/ChangeSummary.test.ts +++ b/full-stack-tests/backend/src/integration/ChangeSummary.test.ts @@ -35,6 +35,7 @@ function getChangeSummaryAsJson(iModel: BriefcaseDb, changeSummaryId: string) { const changeSummary: ChangeSummary = ChangeSummaryManager.queryChangeSummary(iModel, changeSummaryId); const content = { id: changeSummary.id, changeSet: changeSummary.changeSet, instanceChanges: new Array() }; + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId FROM ecchange.change.InstanceChange WHERE Summary.Id=? ORDER BY ECInstanceId", (stmt) => { stmt.bindId(1, changeSummary.id); while (stmt.step() === DbResult.BE_SQLITE_ROW) { @@ -109,6 +110,7 @@ describe("ChangeSummary", () => { ChangeSummaryManager.attachChangeCache(iModel); assert.isTrue(ChangeSummaryManager.isChangeCacheAttached(iModel)); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId,ECClassId,ExtendedProperties FROM change.ChangeSummary ORDER BY ECInstanceId", (myStmt) => { let rowCount: number = 0; while (myStmt.step() === DbResult.BE_SQLITE_ROW) { @@ -120,6 +122,7 @@ describe("ChangeSummary", () => { assert.isAtLeast(rowCount, 3); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECClassId,Summary,WsgId,ParentWsgId,Description,PushDate,UserCreated FROM imodelchange.ChangeSet ORDER BY Summary.Id", (myStmt) => { let rowCount: number = 0; while (myStmt.step() === DbResult.BE_SQLITE_ROW) { @@ -158,6 +161,7 @@ describe("ChangeSummary", () => { ChangeSummaryManager.attachChangeCache(iModel); assert.isTrue(ChangeSummaryManager.isChangeCacheAttached(iModel)); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT WsgId, Summary, ParentWsgId, Description, PushDate, UserCreated FROM imodelchange.ChangeSet", (myStmt) => { assert.equal(myStmt.step(), DbResult.BE_SQLITE_DONE); }); @@ -187,6 +191,7 @@ describe("ChangeSummary", () => { ChangeSummaryManager.attachChangeCache(iModel); assert.isTrue(ChangeSummaryManager.isChangeCacheAttached(iModel)); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT WsgId, Summary, ParentWsgId, Description, PushDate, UserCreated FROM imodelchange.ChangeSet", (myStmt) => { assert.equal(myStmt.step(), DbResult.BE_SQLITE_ROW); const row: any = myStmt.getRow(); @@ -222,6 +227,7 @@ describe("ChangeSummary", () => { ChangeSummaryManager.attachChangeCache(iModel); assert.isTrue(ChangeSummaryManager.isChangeCacheAttached(iModel)); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT WsgId, Summary, ParentWsgId, Description, PushDate, UserCreated FROM imodelchange.ChangeSet ORDER BY Summary.Id", (myStmt) => { assert.equal(myStmt.step(), DbResult.BE_SQLITE_ROW); let row: any = myStmt.getRow(); @@ -268,6 +274,7 @@ describe("ChangeSummary", () => { ChangeSummaryManager.attachChangeCache(iModel); assert.isTrue(ChangeSummaryManager.isChangeCacheAttached(iModel)); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT WsgId, Summary, ParentWsgId, Description, PushDate, UserCreated FROM imodelchange.ChangeSet", (myStmt) => { assert.equal(myStmt.step(), DbResult.BE_SQLITE_ROW); const row: any = myStmt.getRow(); @@ -294,6 +301,7 @@ describe("ChangeSummary", () => { ChangeSummaryManager.attachChangeCache(iModel); assert.isTrue(ChangeSummaryManager.isChangeCacheAttached(iModel)); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT cset.WsgId changesetId FROM change.ChangeSummary csum JOIN imodelchange.ChangeSet cset ON csum.ECInstanceId=cset.Summary.Id ORDER BY csum.ECInstanceId", (myStmt) => { let rowCount: number = 0; while (myStmt.step() === DbResult.BE_SQLITE_ROW) { @@ -333,6 +341,7 @@ describe("ChangeSummary", () => { IModelJsFs.mkdirSync(outDir); const changeSummaries = new Array(); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId FROM ecchange.change.ChangeSummary ORDER BY ECInstanceId", (stmt) => { perfLogger = new PerfLogger("ChangeSummaryManager.queryChangeSummary"); while (stmt.step() === DbResult.BE_SQLITE_ROW) { @@ -349,6 +358,7 @@ describe("ChangeSummary", () => { IModelJsFs.unlinkSync(filePath); const content = { id: changeSummary.id, changeSet: changeSummary.changeSet, instanceChanges: new Array() }; + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId FROM ecchange.change.InstanceChange WHERE Summary.Id=? ORDER BY ECInstanceId", (stmt) => { stmt.bindId(1, changeSummary.id); perfLogger = new PerfLogger(`ChangeSummaryManager.queryInstanceChange for all instances in ChangeSummary ${changeSummary.id}`); @@ -503,9 +513,10 @@ describe("ChangeSummary", () => { // const changeSummaryJson = getChangeSummaryAsJson(iModel, changeSummaryId); // console.log(JSON.stringify(changeSummaryJson, undefined, 2)); // eslint-disable-line + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement( - "SELECT ECInstanceId FROM ecchange.change.InstanceChange WHERE Summary.Id=? ORDER BY ECInstanceId", - (sqlStatement: ECSqlStatement) => { + // eslint-disable-next-line @typescript-eslint/no-deprecated + "SELECT ECInstanceId FROM ecchange.change.InstanceChange WHERE Summary.Id=? ORDER BY ECInstanceId", (sqlStatement: ECSqlStatement) => { sqlStatement.bindId(1, changeSummaryId); while (sqlStatement.step() === DbResult.BE_SQLITE_ROW) { const instanceChangeId = Id64.fromJSON(sqlStatement.getRow().id); @@ -534,6 +545,7 @@ describe("ChangeSummary", () => { ChangeSummaryManager.attachChangeCache(iModel); assert.isTrue(ChangeSummaryManager.isChangeCacheAttached(iModel)); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT WsgId, Summary FROM imodelchange.ChangeSet WHERE Summary.Id=?", (myStmt) => { myStmt.bindId(1, changeSummaryId); assert.equal(myStmt.step(), DbResult.BE_SQLITE_ROW); @@ -557,6 +569,7 @@ describe("ChangeSummary", () => { ChangeSummaryManager.attachChangeCache(iModel); assert.isTrue(ChangeSummaryManager.isChangeCacheAttached(iModel)); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT WsgId, Summary FROM imodelchange.ChangeSet WHERE Summary.Id=?", (myStmt) => { myStmt.bindId(1, changeSummaryId); assert.equal(myStmt.step(), DbResult.BE_SQLITE_ROW); @@ -593,6 +606,7 @@ describe("ChangeSummary", () => { try { ChangeSummaryManager.attachChangeCache(iModel); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId,ECClassId,ExtendedProperties FROM change.ChangeSummary ORDER BY ECInstanceId", (myStmt) => { let rowCount: number = 0; while (myStmt.step() === DbResult.BE_SQLITE_ROW) { @@ -603,6 +617,7 @@ describe("ChangeSummary", () => { assert.isAtLeast(rowCount, 4); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECClassId,Summary,WsgId,ParentWsgId,Description,PushDate,UserCreated FROM imodelchange.ChangeSet ORDER BY Summary.Id", (myStmt) => { let rowCount: number = 0; while (myStmt.step() === DbResult.BE_SQLITE_ROW) { @@ -632,6 +647,7 @@ describe("ChangeSummary", () => { try { ChangeSummaryManager.attachChangeCache(iModel); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECInstanceId,ECClassId,ExtendedProperties FROM change.ChangeSummary ORDER BY ECInstanceId", (myStmt) => { let rowCount: number = 0; while (myStmt.step() === DbResult.BE_SQLITE_ROW) { @@ -642,6 +658,7 @@ describe("ChangeSummary", () => { assert.strictEqual(rowCount, 1); }); + // eslint-disable-next-line @typescript-eslint/no-deprecated iModel.withPreparedStatement("SELECT ECClassId,Summary,WsgId,ParentWsgId,Description,PushDate,UserCreated FROM imodelchange.ChangeSet ORDER BY Summary.Id", (myStmt) => { let rowCount: number = 0; while (myStmt.step() === DbResult.BE_SQLITE_ROW) { diff --git a/full-stack-tests/backend/src/perftest/ECSqlRow.test.ts b/full-stack-tests/backend/src/perftest/ECSqlRow.test.ts index 708f48de9a77..bf7ffd37f075 100644 --- a/full-stack-tests/backend/src/perftest/ECSqlRow.test.ts +++ b/full-stack-tests/backend/src/perftest/ECSqlRow.test.ts @@ -90,6 +90,7 @@ function createElemProps(className: string, _iModelName: IModelDb, modId: Id64St function getCount(imodel: IModelDb, className: string) { let count = 0; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel.withPreparedStatement(`SELECT count(*) AS [count] FROM ${className}`, (stmt: ECSqlStatement) => { assert.equal(DbResult.BE_SQLITE_ROW, stmt.step()); const row = stmt.getRow(); @@ -100,6 +101,7 @@ function getCount(imodel: IModelDb, className: string) { function measureGetRowTime(imodel: IModelDb, className: string): number { let totalTime = 0.0; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel.withPreparedStatement(`SELECT * FROM ${className}`, (stmt: ECSqlStatement) => { while (stmt.step() === DbResult.BE_SQLITE_ROW) { const startTime = new Date().getTime(); diff --git a/full-stack-tests/backend/src/perftest/ElementCRUD.test.ts b/full-stack-tests/backend/src/perftest/ElementCRUD.test.ts index 40b395254e37..61007dd7bce2 100644 --- a/full-stack-tests/backend/src/perftest/ElementCRUD.test.ts +++ b/full-stack-tests/backend/src/perftest/ElementCRUD.test.ts @@ -115,6 +115,7 @@ function verifyProps(testElement: TestElementProps) { function getCount(imodel: IModelDb, className: string) { let count = 0; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel.withPreparedStatement(`SELECT count(*) AS [count] FROM ${className}`, (stmt: ECSqlStatement) => { assert.equal(DbResult.BE_SQLITE_ROW, stmt.step()); const row = stmt.getRow(); diff --git a/full-stack-tests/backend/src/perftest/MixinImpact.test.ts b/full-stack-tests/backend/src/perftest/MixinImpact.test.ts index d33b25035719..93143d285d55 100644 --- a/full-stack-tests/backend/src/perftest/MixinImpact.test.ts +++ b/full-stack-tests/backend/src/perftest/MixinImpact.test.ts @@ -44,6 +44,7 @@ describe("SchemaDesignPerf Impact of Mixins", () => { } function getCount(imodel: IModelDb, className: string) { let count = 0; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel.withPreparedStatement(`SELECT count(*) AS [count] FROM ${className}`, (stmt: ECSqlStatement) => { assert.equal(DbResult.BE_SQLITE_ROW, stmt.step()); const row = stmt.getRow(); @@ -188,6 +189,7 @@ describe("SchemaDesignPerf Impact of Mixins", () => { const perfimodel = IModelTestUtils.createSnapshotFromSeed(testFileName, seedFileName); const startTime = new Date().getTime(); + // eslint-disable-next-line @typescript-eslint/no-deprecated perfimodel.withPreparedStatement("SELECT * FROM tps.MixinElement", (stmt: ECSqlStatement) => { assert.equal(DbResult.BE_SQLITE_ROW, stmt.step()); const row = stmt.getRow(); diff --git a/full-stack-tests/backend/src/perftest/PerfTestUtils.ts b/full-stack-tests/backend/src/perftest/PerfTestUtils.ts index ae2e109b2a5b..15f721ca8eee 100644 --- a/full-stack-tests/backend/src/perftest/PerfTestUtils.ts +++ b/full-stack-tests/backend/src/perftest/PerfTestUtils.ts @@ -155,6 +155,7 @@ export class PerfTestUtility { AND abc.TargetECInstanceId != ec_classid('${schemaName}', '${className}') AND schema.Name != 'BisCore')`; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel.withPreparedStatement(ecsql, (stmt: ECSqlStatement) => { while (DbResult.BE_SQLITE_ROW === stmt.step()) { props.push(stmt.getRow().name); @@ -205,6 +206,7 @@ export class PerfTestUtility { public static getCount(imodel: IModelDb, className: string) { let count = 0; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel.withPreparedStatement(`SELECT count(*) AS [count] FROM ${className}`, (stmt: ECSqlStatement) => { assert.equal(DbResult.BE_SQLITE_ROW, stmt.step()); const row = stmt.getRow(); diff --git a/full-stack-tests/backend/src/perftest/PolymorphicQuery.test.ts b/full-stack-tests/backend/src/perftest/PolymorphicQuery.test.ts index 4ef298ed37f6..067bc67786df 100644 --- a/full-stack-tests/backend/src/perftest/PolymorphicQuery.test.ts +++ b/full-stack-tests/backend/src/perftest/PolymorphicQuery.test.ts @@ -46,6 +46,7 @@ describe("SchemaDesignPerf Polymorphic query", () => { } function getCount(imodel: IModelDb, className: string) { let count = 0; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel.withPreparedStatement(`SELECT count(*) AS [count] FROM ${className}`, (stmt: ECSqlStatement) => { assert.equal(DbResult.BE_SQLITE_ROW, stmt.step()); const row = stmt.getRow(); @@ -227,6 +228,7 @@ describe("SchemaDesignPerf Polymorphic query", () => { try { let sql = "SELECT * from "; sql = `${sql}tps.Child${i.toString()}`; + // eslint-disable-next-line @typescript-eslint/no-deprecated perfimodel.withPreparedStatement(sql, (stmt: ECSqlStatement) => { while (stmt.step() === DbResult.BE_SQLITE_ROW) { const row = stmt.getRow(); @@ -260,6 +262,7 @@ describe("SchemaDesignPerf Polymorphic query", () => { if (i === 0) sql = `${sql}ONLY `; sql = `${sql}tps.TestElement`; + // eslint-disable-next-line @typescript-eslint/no-deprecated perfimodel.withPreparedStatement(sql, (stmt: ECSqlStatement) => { while (stmt.step() === DbResult.BE_SQLITE_ROW) { const row = stmt.getRow(); diff --git a/full-stack-tests/backend/src/perftest/PropertiesImpact.test.ts b/full-stack-tests/backend/src/perftest/PropertiesImpact.test.ts index 78833262a78f..df3451e6ee3e 100644 --- a/full-stack-tests/backend/src/perftest/PropertiesImpact.test.ts +++ b/full-stack-tests/backend/src/perftest/PropertiesImpact.test.ts @@ -38,6 +38,7 @@ function createElemProps(_imodel: IModelDb, modId: Id64String, catId: Id64String } function getCount(imodel: IModelDb, className: string) { let count = 0; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel.withPreparedStatement(`SELECT count(*) AS [count] FROM ${className}`, (stmt: ECSqlStatement) => { assert.equal(DbResult.BE_SQLITE_ROW, stmt.step()); const row = stmt.getRow(); diff --git a/full-stack-tests/backend/src/perftest/RelationshipImpact.test.ts b/full-stack-tests/backend/src/perftest/RelationshipImpact.test.ts index 06378d03b637..78948e8f1c8c 100644 --- a/full-stack-tests/backend/src/perftest/RelationshipImpact.test.ts +++ b/full-stack-tests/backend/src/perftest/RelationshipImpact.test.ts @@ -44,6 +44,7 @@ describe("SchemaDesignPerf Relationship Comparison", () => { } function getCount(imodel: IModelDb, className: string) { let count = 0; + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel.withPreparedStatement(`SELECT COUNT(*) AS [count] FROM ${className}`, (stmt: ECSqlStatement) => { assert.equal(DbResult.BE_SQLITE_ROW, stmt.step()); const row = stmt.getRow(); @@ -129,6 +130,7 @@ describe("SchemaDesignPerf Relationship Comparison", () => { assert.isTrue(Id64.isValidId64(rel1.sourceId), "Relationship does not exist"); assert.isTrue(Id64.isValidId64(rel1.targetId), "Relationship does not exist"); + // eslint-disable-next-line @typescript-eslint/no-deprecated imodel.withPreparedStatement("SELECT * from TestRelationSchema.ADrivesB", (stmt: ECSqlStatement) => { assert.equal(DbResult.BE_SQLITE_ROW, stmt.step()); const row = stmt.getRow(); diff --git a/full-stack-tests/presentation/src/performance/GetElementProperties.test.ts b/full-stack-tests/presentation/src/performance/GetElementProperties.test.ts deleted file mode 100644 index 020fb3b20a45..000000000000 --- a/full-stack-tests/presentation/src/performance/GetElementProperties.test.ts +++ /dev/null @@ -1,302 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Bentley Systems, Incorporated. All rights reserved. - * See LICENSE.md in the project root for license terms and full copyright notice. - *--------------------------------------------------------------------------------------------*/ -/* eslint-disable no-console */ - -import { expect } from "chai"; -import * as fs from "fs"; -import * as os from "os"; -import { join } from "path"; -import { IModelDb, IModelHost, SnapshotDb } from "@itwin/core-backend"; -import { DbResult, Id64String, StopWatch } from "@itwin/core-bentley"; -import { QueryRowFormat } from "@itwin/core-common"; -import { Presentation } from "@itwin/presentation-backend"; - -describe("#performance Element properties loading", () => { - let imodel: SnapshotDb; - let testIModelName: string; - - before(async () => { - if (!process.env.TEST_IMODEL) { - throw new Error("The test requires tested imodel path to be set through TEST_IMODEL environment variable"); - } - if (!fs.existsSync(process.env.TEST_IMODEL)) { - throw new Error(`Test imodel path is set, but the file does not exist (TEST_IMODEL = ${process.env.TEST_IMODEL})`); - } - testIModelName = process.env.TEST_IMODEL; - - await IModelHost.startup({ cacheDir: join(__dirname, ".cache") }); - Presentation.initialize({ - useMmap: true, - workerThreadsCount: os.availableParallelism(), - }); - }); - - after(async () => { - await IModelHost.shutdown(); - Presentation.terminate(); - }); - - beforeEach(() => { - imodel = SnapshotDb.openFile(testIModelName); - expect(imodel).is.not.null; - }); - - afterEach(() => { - imodel.close(); - }); - - it("load properties using 'getElementProperties' with element class name", async function () { - const timer = new StopWatch(undefined, true); - const itemIds = new Set(); - const { total, iterator } = await Presentation.getManager().getElementProperties({ imodel, elementClasses: ["BisCore.GeometricElement"], batchSize: 1000 }); - console.log(`Loading properties for ${total} elements...`); - for await (const items of iterator()) { - items.forEach((item) => itemIds.add(item.id)); - console.log(`Got ${itemIds.size} items. Elapsed: ${timer.currentSeconds} s., Speed: ${(itemIds.size / timer.currentSeconds).toFixed(2)} el./s.`); - } - expect(itemIds.size).to.eq(total); - console.log(`Loaded ${itemIds.size} elements properties in ${timer.currentSeconds.toFixed(2)} s`); - }); - - it("load properties using 'getElementProperties' with element ids", async function () { - const elementIds = new Array(); - for await (const row of imodel.createQueryReader(`SELECT IdToHex(ECInstanceId) id FROM BisCore.GeometricElement`)) { - elementIds.push(row.id); - } - console.log(`Created an array of ${elementIds.length} elements ids`); - - const timer = new StopWatch(undefined, true); - const itemIds = new Set(); - const { total, iterator } = await Presentation.getManager().getElementProperties({ imodel, elementIds, batchSize: 1000 }); - console.log(`Loading properties for ${total} elements...`); - for await (const items of iterator()) { - items.forEach((item) => itemIds.add(item.id)); - console.log(`Got ${itemIds.size} items. Elapsed: ${timer.currentSeconds} s., Speed: ${(itemIds.size / timer.currentSeconds).toFixed(2)} el./s.`); - } - expect(itemIds.size).to.eq(total); - console.log(`Loaded ${itemIds.size} elements properties in ${timer.currentSeconds.toFixed(2)} s`); - }); - - it("load properties using ECSQL", async function () { - const timer = new StopWatch(undefined, true); - process.stdout.write(`Loading properties.`); - let itemsCount = 0; - for await (const _properties of getElementsPropertiesECSQL(imodel)) { - itemsCount++; - if (itemsCount % 1000 === 0) { - process.stdout.write("."); - } - } - process.stdout.write(`\nLoaded ${itemsCount} elements properties in ${timer.currentSeconds.toFixed(2)} s`); - }); -}); - -async function* getElementsPropertiesECSQL(db: IModelDb) { - const query = ` - SELECT el.ECInstanceId id, '[' || schemaDef.Name || '].[' || classDef.Name || ']' className - FROM bis.GeometricElement el - JOIN meta.ECClassDef classDef ON classDef.ECInstanceId = el.ECClassId - JOIN meta.ECSchemaDef schemaDef ON schemaDef.ECInstanceId = classDef.Schema.Id`; - - for await (const row of db.createQueryReader(query, undefined, { abbreviateBlobs: true, rowFormat: QueryRowFormat.UseJsPropertyNames })) { - const properties = loadElementProperties(db, row.className, row.id); - expect(properties.id).to.be.eq(row.id); - yield properties; - } -} - -function loadElementProperties(db: IModelDb, className: string, elementId: string) { - const elementProperties = loadProperties(db, className, [elementId], true); - return { - ...elementProperties[0], - ...loadRelatedProperties(db, () => queryGeometricElement3dTypeDefinitions(db, elementId), true), - ...loadRelatedProperties(db, () => queryGeometricElement2dTypeDefinitions(db, elementId), true), - ...loadRelatedProperties(db, () => queryElementLinks(db, elementId), false), - ...loadRelatedProperties(db, () => queryGroupElementLinks(db, elementId), false), - ...loadRelatedProperties(db, () => queryModelLinks(db, elementId), false), - ...loadRelatedProperties(db, () => queryDrawingGraphicElements(db, elementId), false), - ...loadRelatedProperties(db, () => queryGraphicalElement3dElements(db, elementId), false), - ...loadRelatedProperties(db, () => queryExternalSourceRepositories(db, elementId), false), - ...loadRelatedProperties(db, () => queryExternalSourceGroupRepositories(db, elementId), false), - }; -} - -function loadRelatedProperties(db: IModelDb, idsGetter: () => Map, loadAspects: boolean) { - const idsByClass = idsGetter(); - const properties: any = {}; - for (const entry of idsByClass) { - properties[entry[0]] = loadProperties(db, entry[0], entry[1], loadAspects); - } - return properties; -} - -function loadProperties(db: IModelDb, className: string, ids: string[], loadAspects: boolean) { - const query = ` - SELECT * - FROM ${className} - WHERE ECInstanceId IN (${ids.map((_v, idx) => `:id${idx}`).join(",")})`; - - return db.withPreparedStatement(query, (stmt) => { - const properties: any[] = []; - stmt.bindValues(ids); - while (stmt.step() === DbResult.BE_SQLITE_ROW) { - const row = stmt.getRow(); - properties.push({ - ...collectProperties(row), - ...(loadAspects ? loadRelatedProperties(db, () => queryUniqueAspects(db, row.Id), false) : {}), - ...(loadAspects ? loadRelatedProperties(db, () => queryMultiAspects(db, row.Id), false) : {}), - }); - } - return properties; - }); -} - -function queryMultiAspects(db: IModelDb, elementId: string) { - const query = ` - SELECT ma.ECInstanceId id, '[' || schemaDef.Name || '].[' || classDef.Name || ']' className - FROM bis.ElementMultiAspect ma - JOIN meta.ECClassDef classDef ON classDef.ECInstanceId = ma.ECClassId - JOIN meta.ECSchemaDef schemaDef ON schemaDef.ECInstanceId = classDef.Schema.Id - WHERE ma.Element.Id = :id`; - return queryRelatedClasses(db, query, { id: elementId }); -} - -function queryUniqueAspects(db: IModelDb, elementId: string) { - const query = ` - SELECT ua.ECInstanceId id, '[' || schemaDef.Name || '].[' || classDef.Name || ']' className - FROM bis.ElementUniqueAspect ua - JOIN meta.ECClassDef classDef ON classDef.ECInstanceId = ua.ECClassId - JOIN meta.ECSchemaDef schemaDef ON schemaDef.ECInstanceId = classDef.Schema.Id - WHERE ua.Element.Id = :id`; - return queryRelatedClasses(db, query, { id: elementId }); -} - -function queryGeometricElement3dTypeDefinitions(db: IModelDb, elementId: string) { - const query = ` - SELECT relType.TargetECInstanceId id, '[' || schemaDef.Name || '].[' || classDef.Name || ']' className - FROM bis.GeometricElement3dHasTypeDefinition relType - JOIN meta.ECClassDef classDef ON classDef.ECInstanceId = relType.TargetECClassId - JOIN meta.ECSchemaDef schemaDef ON schemaDef.ECInstanceId = classDef.Schema.Id - WHERE relType.SourceECInstanceId = :id`; - return queryRelatedClasses(db, query, { id: elementId }); -} - -function queryGeometricElement2dTypeDefinitions(db: IModelDb, elementId: string) { - const query = ` - SELECT relType.TargetECInstanceId id, '[' || schemaDef.Name || '].[' || classDef.Name || ']' className - FROM bis.GeometricElement2dHasTypeDefinition relType - JOIN meta.ECClassDef classDef ON classDef.ECInstanceId = relType.TargetECClassId - JOIN meta.ECSchemaDef schemaDef ON schemaDef.ECInstanceId = classDef.Schema.Id - WHERE relType.SourceECInstanceId = :id`; - return queryRelatedClasses(db, query, { id: elementId }); -} - -function queryElementLinks(db: IModelDb, elementId: string) { - const query = ` - SELECT relLink.TargetECInstanceId id, '[' || schemaDef.Name || '].[' || classDef.Name || ']' className - FROM bis.ElementHasLinks relLink - JOIN meta.ECClassDef classDef ON classDef.ECInstanceId = relLink.TargetECClassId - JOIN meta.ECSchemaDef schemaDef ON schemaDef.ECInstanceId = classDef.Schema.Id - WHERE relLink.SourceECInstanceId = :id`; - return queryRelatedClasses(db, query, { id: elementId }); -} - -function queryGroupElementLinks(db: IModelDb, elementId: string) { - const query = ` - SELECT relLink.TargetECInstanceId id, '[' || schemaDef.Name || '].[' || classDef.Name || ']' className - FROM bis.ElementHasLinks relLink - JOIN bis.ElementGroupsMembers relElementGroup ON relElementGroup.SourceECInstanceId = relLink.SourceECInstanceId - JOIN meta.ECClassDef classDef ON classDef.ECInstanceId = relLink.TargetECClassId - JOIN meta.ECSchemaDef schemaDef ON schemaDef.ECInstanceId = classDef.Schema.Id - WHERE relElementGroup.TargetECInstanceId = :id`; - return queryRelatedClasses(db, query, { id: elementId }); -} - -function queryModelLinks(db: IModelDb, elementId: string) { - const query = ` - SELECT relLink.TargetECInstanceId id, '[' || schemaDef.Name || '].[' || classDef.Name || ']' className - FROM bis.ElementHasLinks relLink - JOIN bis.ModelModelsElement relModelModels ON relModelModels.TargetECInstanceId = relLink.SourceECInstanceId - JOIN bis.ModelContainsElements relModelContains ON relModelContains.SourceECInstanceId = relModelModels.SourceECInstanceId - JOIN meta.ECClassDef classDef ON classDef.ECInstanceId = relLink.TargetECClassId - JOIN meta.ECSchemaDef schemaDef ON schemaDef.ECInstanceId = classDef.Schema.Id - WHERE relModelContains.TargetECInstanceId = :id`; - return queryRelatedClasses(db, query, { id: elementId }); -} - -function queryDrawingGraphicElements(db: IModelDb, elementId: string) { - const query = ` - SELECT relRepresents.SourceECInstanceId id, '[' || schemaDef.Name || '].[' || classDef.Name || ']' className - FROM bis.DrawingGraphicRepresentsElement relRepresents - JOIN meta.ECClassDef classDef ON classDef.ECInstanceId = relRepresents.SourceECClassId - JOIN meta.ECSchemaDef schemaDef ON schemaDef.ECInstanceId = classDef.Schema.Id - WHERE relRepresents.TargetECInstanceId = :id`; - return queryRelatedClasses(db, query, { id: elementId }); -} - -function queryGraphicalElement3dElements(db: IModelDb, elementId: string) { - const query = ` - SELECT relRepresents.SourceECInstanceId id, '[' || schemaDef.Name || '].[' || classDef.Name || ']' className - FROM bis.GraphicalElement3dRepresentsElement relRepresents - JOIN meta.ECClassDef classDef ON classDef.ECInstanceId = relRepresents.SourceECClassId - JOIN meta.ECSchemaDef schemaDef ON schemaDef.ECInstanceId = classDef.Schema.Id - WHERE relRepresents.TargetECInstanceId = :id`; - return queryRelatedClasses(db, query, { id: elementId }); -} - -function queryExternalSourceRepositories(db: IModelDb, elementId: string) { - const query = ` - SELECT relRepository.TargetECInstanceId id, '[' || schemaDef.Name || '].[' || classDef.Name || ']' className - FROM bis.ExternalSourceIsInRepository relRepository - JOIN bis.ElementIsFromSource relFromSource ON relFromSource.TargetECInstanceId = relRepository.SourceECInstanceId - JOIN bis.ExternalSourceAspect aspect ON aspect.ECInstanceId = relFromSource.SourceECInstanceId - JOIN meta.ECClassDef classDef ON classDef.ECInstanceId = relRepository.TargetECClassId - JOIN meta.ECSchemaDef schemaDef ON schemaDef.ECInstanceId = classDef.Schema.Id - WHERE aspect.Element.Id = :id`; - return queryRelatedClasses(db, query, { id: elementId }); -} - -function queryExternalSourceGroupRepositories(db: IModelDb, elementId: string) { - const query = ` - SELECT relRepository.TargetECInstanceId id, '[' || schemaDef.Name || '].[' || classDef.Name || ']' className - FROM bis.ExternalSourceIsInRepository relRepository - JOIN bis.ExternalSourceGroupGroupsSources relGroupSources ON relGroupSources.TargetECInstanceId = relRepository.SourceECInstanceId - JOIN bis.ElementIsFromSource relFromSource ON relFromSource.TargetECInstanceId = relGroupSources.SourceECInstanceId AND relFromSource.TargetECClassId IS (bis.ExternalSourceGroup) - JOIN bis.ExternalSourceAspect aspect ON aspect.ECInstanceId = relFromSource.SourceECInstanceId - JOIN meta.ECClassDef classDef ON classDef.ECInstanceId = relRepository.TargetECClassId - JOIN meta.ECSchemaDef schemaDef ON schemaDef.ECInstanceId = classDef.Schema.Id - WHERE aspect.Element.Id = :id`; - return queryRelatedClasses(db, query, { id: elementId }); -} - -function queryRelatedClasses(db: IModelDb, query: string, bindings: object) { - return db.withPreparedStatement(query, (stmt) => { - stmt.bindValues(bindings); - const relatedClasses = new Map(); - while (stmt.step() === DbResult.BE_SQLITE_ROW) { - const row = stmt.getRow(); - const className = row.className; - const relatedIds = relatedClasses.get(className); - if (!relatedIds) { - relatedClasses.set(className, [row.id]); - continue; - } - relatedIds.push(row.id); - } - return relatedClasses; - }); -} - -const excludedProperties = new Set(["element", "jsonProperties", "geometryStream"]); -function collectProperties(row: any) { - const element: any = {}; - for (const prop in row) { - if (excludedProperties.has(prop)) { - continue; - } - element[prop] = row[prop]; - } - return element; -} diff --git a/presentation/backend/src/presentation-backend/ElementPropertiesHelper.ts b/presentation/backend/src/presentation-backend/ElementPropertiesHelper.ts index 4f7c5d019c81..164510473622 100644 --- a/presentation/backend/src/presentation-backend/ElementPropertiesHelper.ts +++ b/presentation/backend/src/presentation-backend/ElementPropertiesHelper.ts @@ -7,8 +7,8 @@ */ import { bufferCount, defer, from, groupBy, map, mergeMap, Observable, ObservedValueOf, of, range, reduce } from "rxjs"; -import { ECSqlStatement, IModelDb } from "@itwin/core-backend"; -import { DbResult, Id64, Id64Array, Id64String, OrderedId64Iterable } from "@itwin/core-bentley"; +import { IModelDb } from "@itwin/core-backend"; +import { Id64, Id64Array, Id64String, OrderedId64Iterable } from "@itwin/core-bentley"; import { QueryRowProxy } from "@itwin/core-common"; import { ContentDescriptorRequestOptions, @@ -93,7 +93,7 @@ export function getContentItemsObservableFromClassNames( classParallelism, ), ), - count: of(getElementsCount(imodel, elementClasses)), + count: from(getElementsCount(imodel, elementClasses)), }; } @@ -278,7 +278,7 @@ export function getBatchedClassElementIds(imodel: IModelDb, fullClassName: strin } /** @internal */ -export function getElementsCount(db: IModelDb, classNames: string[]) { +export async function getElementsCount(db: IModelDb, classNames: string[]) { const whereClause = (() => { if (classNames === undefined || classNames.length === 0) { return undefined; @@ -296,11 +296,12 @@ export function getElementsCount(db: IModelDb, classNames: string[]) { return `e.ECClassId IS (${classNames.join(",")})`; })(); const query = ` - SELECT COUNT(e.ECInstanceId) + SELECT COUNT(e.ECInstanceId) AS elementCount FROM bis.Element e ${whereClause ? `WHERE ${whereClause}` : ""} `; - return db.withPreparedStatement(query, (stmt: ECSqlStatement) => { - return stmt.step() === DbResult.BE_SQLITE_ROW ? stmt.getValue(0).getInteger() : 0; - }); + for await (const row of db.createQueryReader(query)) { + return row.elementCount; + } + return 0; } diff --git a/presentation/backend/src/presentation-backend/RulesetEmbedder.ts b/presentation/backend/src/presentation-backend/RulesetEmbedder.ts index 06354a8588b9..b8aefc199c85 100644 --- a/presentation/backend/src/presentation-backend/RulesetEmbedder.ts +++ b/presentation/backend/src/presentation-backend/RulesetEmbedder.ts @@ -8,19 +8,8 @@ import * as path from "path"; import { gt as versionGt, gte as versionGte, lt as versionLt } from "semver"; -import { - DefinitionElement, - DefinitionModel, - DefinitionPartition, - ECSqlStatement, - Element, - Entity, - IModelDb, - KnownLocations, - Model, - Subject, -} from "@itwin/core-backend"; -import { assert, DbResult, Id64String } from "@itwin/core-bentley"; +import { DefinitionElement, DefinitionModel, DefinitionPartition, Element, Entity, IModelDb, KnownLocations, Model, Subject } from "@itwin/core-backend"; +import { assert, Id64String } from "@itwin/core-bentley"; import { BisCodeSpec, Code, @@ -243,14 +232,11 @@ export class RulesetEmbedder { } const rulesetList: Ruleset[] = []; - this._imodel.withPreparedStatement(`SELECT ECInstanceId AS id FROM ${RulesetElements.Ruleset.classFullName}`, (statement: ECSqlStatement) => { - while (DbResult.BE_SQLITE_ROW === statement.step()) { - const row = statement.getRow(); - const rulesetElement = this._imodel.elements.getElement({ id: row.id }); - const ruleset = rulesetElement.jsonProperties.jsonProperties; - rulesetList.push(ruleset); - } - }); + for await (const row of this._imodel.createQueryReader(`SELECT ECInstanceId AS id FROM ${RulesetElements.Ruleset.classFullName}`)) { + const rulesetElement = this._imodel.elements.getElement({ id: row.id }); + const ruleset = rulesetElement.jsonProperties.jsonProperties; + rulesetList.push(ruleset); + } return rulesetList; } diff --git a/presentation/backend/src/presentation-backend/SelectionScopesHelper.ts b/presentation/backend/src/presentation-backend/SelectionScopesHelper.ts index 65d71c0b922f..4eadab853717 100644 --- a/presentation/backend/src/presentation-backend/SelectionScopesHelper.ts +++ b/presentation/backend/src/presentation-backend/SelectionScopesHelper.ts @@ -7,7 +7,8 @@ */ import { GeometricElement, IModelDb } from "@itwin/core-backend"; -import { DbResult, Id64, Id64String } from "@itwin/core-bentley"; +import { Id64, Id64Array, Id64String } from "@itwin/core-bentley"; +import { QueryBinder } from "@itwin/core-common"; import { ComputeSelectionRequestOptions, ElementSelectionScopeProps, @@ -58,46 +59,40 @@ export class SelectionScopesHelper { return getElementKey(iModel, currId); } - private static computeElementSelection(iModel: IModelDb, elementIds: Id64String[], ancestorLevel: number) { + private static async computeElementSelection(iModel: IModelDb, elementIds: Id64String[], ancestorLevel: number) { const parentKeys = new KeySet(); - elementIds.forEach( - skipTransients((id) => { - const key = this.getElementKey(iModel, id, ancestorLevel); - key && parentKeys.add(key); - }), - ); + await forEachNonTransientId(elementIds, async (id) => { + const key = this.getElementKey(iModel, id, ancestorLevel); + key && parentKeys.add(key); + }); return parentKeys; } - private static computeCategorySelection(iModel: IModelDb, ids: Id64String[]) { + private static async computeCategorySelection(iModel: IModelDb, ids: Id64String[]) { const categoryKeys = new KeySet(); - ids.forEach( - skipTransients((id) => { - const el = iModel.elements.tryGetElement(id); - const category = el?.category ? iModel.elements.tryGetElementProps(el.category) : undefined; - if (category) { - categoryKeys.add({ className: category.classFullName, id: category.id! }); - } - }), - ); + await forEachNonTransientId(ids, async (id) => { + const el = iModel.elements.tryGetElement(id); + const category = el?.category ? iModel.elements.tryGetElementProps(el.category) : undefined; + if (category) { + categoryKeys.add({ className: category.classFullName, id: category.id! }); + } + }); return categoryKeys; } - private static computeModelSelection(iModel: IModelDb, ids: Id64String[]) { + private static async computeModelSelection(iModel: IModelDb, ids: Id64String[]) { const modelKeys = new KeySet(); - ids.forEach( - skipTransients((id) => { - const el = iModel.elements.tryGetElementProps(id); - const model = el ? iModel.models.tryGetModelProps(el.model) : undefined; - if (model) { - modelKeys.add({ className: model.classFullName, id: model.id! }); - } - }), - ); + await forEachNonTransientId(ids, async (id) => { + const el = iModel.elements.tryGetElementProps(id); + const model = el ? iModel.models.tryGetModelProps(el.model) : undefined; + if (model) { + modelKeys.add({ className: model.classFullName, id: model.id! }); + } + }); return modelKeys; } - private static getRelatedFunctionalElementKey(imodel: IModelDb, graphicalElementId: Id64String): InstanceKey | undefined { + private static async getRelatedFunctionalElementKey(imodel: IModelDb, graphicalElementId: Id64String): Promise { const query = ` SELECT funcSchemaDef.Name || '.' || funcClassDef.Name funcElClassName, fe.ECInstanceId funcElId FROM bis.Element e @@ -106,25 +101,24 @@ export class SelectionScopesHelper { LEFT JOIN func.FunctionalElement fe ON fe.ECInstanceId IN (rel1.TargetECInstanceId, rel2.TargetECInstanceId) INNER JOIN meta.ECClassDef funcClassDef ON funcClassDef.ECInstanceId = fe.ECClassId INNER JOIN meta.ECSchemaDef funcSchemaDef ON funcSchemaDef.ECInstanceId = funcClassDef.Schema.Id - WHERE e.ECInstanceId = ? - `; - return imodel.withPreparedStatement(query, (stmt): InstanceKey | undefined => { - stmt.bindId(1, graphicalElementId); - // istanbul ignore else - if (DbResult.BE_SQLITE_ROW === stmt.step()) { - const row = stmt.getRow(); - if (row.funcElClassName && row.funcElId) { - return { className: row.funcElClassName.replace(".", ":"), id: row.funcElId }; - } + WHERE e.ECInstanceId = ? + `; + + const bindings = new QueryBinder(); + bindings.bindId(1, graphicalElementId); + + for await (const row of imodel.createQueryReader(query, bindings)) { + if (row.funcElClassName && row.funcElId) { + return { className: row.funcElClassName.replace(".", ":"), id: row.funcElId }; } - return undefined; - }); + } + return undefined; } - private static findFirstRelatedFunctionalElementKey(imodel: IModelDb, graphicalElementId: Id64String): InstanceKey | undefined { + private static async findFirstRelatedFunctionalElementKey(imodel: IModelDb, graphicalElementId: Id64String): Promise { let currId: Id64String | undefined = graphicalElementId; while (currId) { - const relatedFunctionalKey = this.getRelatedFunctionalElementKey(imodel, currId); + const relatedFunctionalKey = await this.getRelatedFunctionalElementKey(imodel, currId); if (relatedFunctionalKey) { return relatedFunctionalKey; } @@ -133,7 +127,7 @@ export class SelectionScopesHelper { return undefined; } - private static elementClassDerivesFrom(imodel: IModelDb, elementId: Id64String, baseClassFullName: string): boolean { + private static async elementClassDerivesFrom(imodel: IModelDb, elementId: Id64String, baseClassFullName: string): Promise { const query = ` SELECT 1 FROM bis.Element e @@ -141,96 +135,94 @@ export class SelectionScopesHelper { INNER JOIN meta.ECClassDef baseClass ON baseClass.ECInstanceId = baseClassRels.TargetECInstanceId INNER JOIN meta.ECSchemaDef baseSchema ON baseSchema.ECInstanceId = baseClass.Schema.Id WHERE e.ECInstanceId = ? AND (baseSchema.Name || ':' || baseClass.Name) = ? - `; - return imodel.withPreparedStatement(query, (stmt): boolean => { - stmt.bindId(1, elementId); - stmt.bindString(2, baseClassFullName); - return DbResult.BE_SQLITE_ROW === stmt.step(); - }); + `; + + const bindings = new QueryBinder(); + bindings.bindId(1, elementId); + bindings.bindString(2, baseClassFullName); + + for await (const _ of imodel.createQueryReader(query, bindings)) { + return true; + } + return false; } - private static computeFunctionalElementSelection(iModel: IModelDb, ids: Id64String[]) { + private static async computeFunctionalElementSelection(iModel: IModelDb, ids: Id64String[]) { const keys = new KeySet(); - ids.forEach( - skipTransients((id): void => { - const is3d = this.elementClassDerivesFrom(iModel, id, "BisCore.GeometricElement3d"); - if (!is3d) { - // if the input is not a 3d element, we try to find the first related functional element - const firstFunctionalKey = this.findFirstRelatedFunctionalElementKey(iModel, id); - if (firstFunctionalKey) { - keys.add(firstFunctionalKey); - return; - } - } - let keyToAdd: InstanceKey | undefined; - if (is3d) { - // if we're computing scope for a 3d element, try to switch to its related functional element - keyToAdd = this.getRelatedFunctionalElementKey(iModel, id); + await forEachNonTransientId(ids, async (id) => { + const is3d = await this.elementClassDerivesFrom(iModel, id, "BisCore.GeometricElement3d"); + if (!is3d) { + // if the input is not a 3d element, we try to find the first related functional element + const firstFunctionalKey = await this.findFirstRelatedFunctionalElementKey(iModel, id); + if (firstFunctionalKey) { + keys.add(firstFunctionalKey); + return; } - if (!keyToAdd) { - keyToAdd = getElementKey(iModel, id); - } - keyToAdd && keys.add(keyToAdd); - }), - ); + } + let keyToAdd: InstanceKey | undefined; + if (is3d) { + // if we're computing scope for a 3d element, try to switch to its related functional element + keyToAdd = await this.getRelatedFunctionalElementKey(iModel, id); + } + if (!keyToAdd) { + keyToAdd = getElementKey(iModel, id); + } + keyToAdd && keys.add(keyToAdd); + }); return keys; } - private static computeFunctionalAssemblySelection(iModel: IModelDb, ids: Id64String[]) { + private static async computeFunctionalAssemblySelection(iModel: IModelDb, ids: Id64String[]) { const keys = new KeySet(); - ids.forEach( - skipTransients((id): void => { - let idToGetAssemblyFor = id; - const is3d = this.elementClassDerivesFrom(iModel, id, "BisCore.GeometricElement3d"); - if (!is3d) { - // if the input is not a 3d element, we try to find the first related functional element - const firstFunctionalKey = this.findFirstRelatedFunctionalElementKey(iModel, id); - if (firstFunctionalKey) { - idToGetAssemblyFor = firstFunctionalKey.id; - } + await forEachNonTransientId(ids, async (id) => { + let idToGetAssemblyFor = id; + const is3d = await this.elementClassDerivesFrom(iModel, id, "BisCore.GeometricElement3d"); + if (!is3d) { + // if the input is not a 3d element, we try to find the first related functional element + const firstFunctionalKey = await this.findFirstRelatedFunctionalElementKey(iModel, id); + if (firstFunctionalKey) { + idToGetAssemblyFor = firstFunctionalKey.id; } - // find the assembly of either the given element or the functional element - const assemblyKey = this.getElementKey(iModel, idToGetAssemblyFor, 1); - let keyToAdd = assemblyKey; - if (is3d && keyToAdd) { - // if we're computing scope for a 3d element, try to switch to its related functional element - const relatedFunctionalKey = this.getRelatedFunctionalElementKey(iModel, keyToAdd.id); - if (relatedFunctionalKey) { - keyToAdd = relatedFunctionalKey; - } + } + // find the assembly of either the given element or the functional element + const assemblyKey = this.getElementKey(iModel, idToGetAssemblyFor, 1); + let keyToAdd = assemblyKey; + if (is3d && keyToAdd) { + // if we're computing scope for a 3d element, try to switch to its related functional element + const relatedFunctionalKey = await this.getRelatedFunctionalElementKey(iModel, keyToAdd.id); + if (relatedFunctionalKey) { + keyToAdd = relatedFunctionalKey; } - keyToAdd && keys.add(keyToAdd); - }), - ); + } + keyToAdd && keys.add(keyToAdd); + }); return keys; } - private static computeFunctionalTopAssemblySelection(iModel: IModelDb, ids: Id64String[]) { + private static async computeFunctionalTopAssemblySelection(iModel: IModelDb, ids: Id64String[]) { const keys = new KeySet(); - ids.forEach( - skipTransients((id): void => { - let idToGetAssemblyFor = id; - const is3d = this.elementClassDerivesFrom(iModel, id, "BisCore.GeometricElement3d"); - if (!is3d) { - // if the input is not a 3d element, we try to find the first related functional element - const firstFunctionalKey = this.findFirstRelatedFunctionalElementKey(iModel, id); - if (firstFunctionalKey) { - idToGetAssemblyFor = firstFunctionalKey.id; - } + await forEachNonTransientId(ids, async (id) => { + let idToGetAssemblyFor = id; + const is3d = await this.elementClassDerivesFrom(iModel, id, "BisCore.GeometricElement3d"); + if (!is3d) { + // if the input is not a 3d element, we try to find the first related functional element + const firstFunctionalKey = await this.findFirstRelatedFunctionalElementKey(iModel, id); + if (firstFunctionalKey) { + idToGetAssemblyFor = firstFunctionalKey.id; } - // find the top assembly of either the given element or the functional element - const topAssemblyKey = this.getElementKey(iModel, idToGetAssemblyFor, Number.MAX_SAFE_INTEGER); - let keyToAdd = topAssemblyKey; - if (is3d && keyToAdd) { - // if we're computing scope for a 3d element, try to switch to its related functional element - const relatedFunctionalKey = this.getRelatedFunctionalElementKey(iModel, keyToAdd.id); - if (relatedFunctionalKey) { - keyToAdd = relatedFunctionalKey; - } + } + // find the top assembly of either the given element or the functional element + const topAssemblyKey = this.getElementKey(iModel, idToGetAssemblyFor, Number.MAX_SAFE_INTEGER); + let keyToAdd = topAssemblyKey; + if (is3d && keyToAdd) { + // if we're computing scope for a 3d element, try to switch to its related functional element + const relatedFunctionalKey = await this.getRelatedFunctionalElementKey(iModel, keyToAdd.id); + if (relatedFunctionalKey) { + keyToAdd = relatedFunctionalKey; } - keyToAdd && keys.add(keyToAdd); - }), - ); + } + keyToAdd && keys.add(keyToAdd); + }); return keys; } @@ -276,10 +268,6 @@ export class SelectionScopesHelper { } } -const skipTransients = (callback: (id: Id64String) => void) => { - return (id: Id64String) => { - if (!Id64.isTransient(id)) { - callback(id); - } - }; -}; +async function forEachNonTransientId(ids: Id64Array, callback: (id: Id64String) => Promise): Promise { + await Promise.all(ids.filter((id) => !Id64.isTransient(id)).map(callback)); +} diff --git a/presentation/backend/src/presentation-backend/Utils.ts b/presentation/backend/src/presentation-backend/Utils.ts index 787df30a2767..805bd630b651 100644 --- a/presentation/backend/src/presentation-backend/Utils.ts +++ b/presentation/backend/src/presentation-backend/Utils.ts @@ -8,7 +8,7 @@ import { parse as parseVersion } from "semver"; import { IModelDb } from "@itwin/core-backend"; -import { DbResult, Id64String } from "@itwin/core-bentley"; +import { Id64String } from "@itwin/core-bentley"; import { combineDiagnosticsSeverities, compareDiagnosticsSeverities, @@ -39,17 +39,8 @@ export function getLocalizedStringEN(key: string) { /** @internal */ export function getElementKey(imodel: IModelDb, id: Id64String): InstanceKey | undefined { - let key: InstanceKey | undefined; - const query = `SELECT ECClassId FROM BisCore.Element e WHERE ECInstanceId = ?`; - imodel.withPreparedStatement(query, (stmt) => { - try { - stmt.bindId(1, id); - if (stmt.step() === DbResult.BE_SQLITE_ROW) { - key = { className: stmt.getValue(0).getClassNameForClassId().replace(".", ":"), id }; - } - } catch {} - }); - return key; + const className = imodel.elements.tryGetElementProps(id)?.classFullName; + return className ? { className, id } : undefined; } /** @internal */ diff --git a/presentation/backend/src/test/ElementPropertiesHelper.test.ts b/presentation/backend/src/test/ElementPropertiesHelper.test.ts index bfde9c553fcf..d61885630134 100644 --- a/presentation/backend/src/test/ElementPropertiesHelper.test.ts +++ b/presentation/backend/src/test/ElementPropertiesHelper.test.ts @@ -5,8 +5,7 @@ import { expect } from "chai"; import { firstValueFrom, toArray } from "rxjs"; import * as moq from "typemoq"; -import { ECSqlStatement, ECSqlValue, IModelDb } from "@itwin/core-backend"; -import { DbResult } from "@itwin/core-bentley"; +import { IModelDb } from "@itwin/core-backend"; import { PresentationError } from "@itwin/presentation-common"; import { createIdBatches, getBatchedClassElementIds, getElementsCount } from "../presentation-backend/ElementPropertiesHelper"; import { stubECSqlReader } from "./Helpers"; @@ -17,50 +16,30 @@ describe("getElementsCount", () => { imodelMock.reset(); }); - it("returns 0 when statement has no rows", () => { - imodelMock - .setup((x) => x.withPreparedStatement(moq.It.isAnyString(), moq.It.isAny())) - .returns((_q, cb) => { - const statementMock = moq.Mock.ofType(); - statementMock.setup((x) => x.step()).returns(() => DbResult.BE_SQLITE_DONE); - return cb(statementMock.object); - }); - expect(getElementsCount(imodelMock.object, [])).to.be.eq(0); + it("returns 0 when statement has no rows", async () => { + imodelMock.setup((x) => x.createQueryReader(moq.It.isAnyString())).returns(() => stubECSqlReader([])); + expect(await getElementsCount(imodelMock.object, [])).to.be.eq(0); }); - it("returns count when statement has row", () => { + it("returns count when statement has row", async () => { const elementCount = 3; - imodelMock - .setup((x) => x.withPreparedStatement(moq.It.isAnyString(), moq.It.isAny())) - .returns((_q, cb) => { - const valueMock = moq.Mock.ofType(); - valueMock.setup((x) => x.getInteger()).returns(() => elementCount); - const statementMock = moq.Mock.ofType(); - statementMock.setup((x) => x.step()).returns(() => DbResult.BE_SQLITE_ROW); - statementMock.setup((x) => x.getValue(0)).returns(() => valueMock.object); - return cb(statementMock.object); - }); - expect(getElementsCount(imodelMock.object, [])).to.be.eq(elementCount); + imodelMock.setup((x) => x.createQueryReader(moq.It.isAnyString())).returns(() => stubECSqlReader([{ elementCount }])); + expect(await getElementsCount(imodelMock.object, [])).to.be.eq(elementCount); }); - it("adds WHERE clause when class list is defined and not empty", () => { + it("adds WHERE clause when class list is defined and not empty", async () => { imodelMock - .setup((x) => - x.withPreparedStatement( - moq.It.is((query) => query.includes("WHERE")), - moq.It.isAny(), - ), - ) - .returns(() => 0) + .setup((x) => x.createQueryReader(moq.It.is((query) => query.includes("WHERE")))) + .returns(() => stubECSqlReader([])) .verifiable(); - getElementsCount(imodelMock.object, ["TestSchema:TestClass"]); + await getElementsCount(imodelMock.object, ["TestSchema:TestClass"]); imodelMock.verifyAll(); }); - it("throws if class list contains invalid class name", () => { - expect(() => getElementsCount(imodelMock.object, ["'TestSchema:TestClass'"])).to.throw(PresentationError); - expect(() => getElementsCount(imodelMock.object, ["%TestSchema:TestClass%"])).to.throw(PresentationError); - expect(() => getElementsCount(imodelMock.object, ["TestSchema:TestClass "])).to.throw(PresentationError); + it("throws if class list contains invalid class name", async () => { + await expect(getElementsCount(imodelMock.object, ["'TestSchema:TestClass'"])).to.eventually.be.rejectedWith(PresentationError); + await expect(getElementsCount(imodelMock.object, ["%TestSchema:TestClass%"])).to.eventually.be.rejectedWith(PresentationError); + await expect(getElementsCount(imodelMock.object, ["TestSchema:TestClass "])).to.eventually.be.rejectedWith(PresentationError); }); }); diff --git a/presentation/backend/src/test/PresentationManager.test.ts b/presentation/backend/src/test/PresentationManager.test.ts index 4903bca438e9..a3787f515929 100644 --- a/presentation/backend/src/test/PresentationManager.test.ts +++ b/presentation/backend/src/test/PresentationManager.test.ts @@ -8,8 +8,8 @@ import * as faker from "faker"; import * as path from "path"; import * as sinon from "sinon"; import * as moq from "typemoq"; -import { ECSqlStatement, ECSqlValue, IModelDb, IModelHost, IModelJsNative, IModelNative, IpcHost } from "@itwin/core-backend"; -import { DbResult, Id64, Id64String } from "@itwin/core-bentley"; +import { IModelDb, IModelHost, IModelJsNative, IModelNative, IpcHost } from "@itwin/core-backend"; +import { Id64, Id64String } from "@itwin/core-bentley"; import { SchemaContext } from "@itwin/ecschema-metadata"; import { ArrayTypeDescription, @@ -127,27 +127,15 @@ describe("PresentationManager", () => { await IModelHost.shutdown(); }); - const setupIModelForElementKey = (imodelMock: moq.IMock, key: InstanceKey) => { + const setupIModelForElementKey = (imodelMock: moq.IMock, key: InstanceKey | undefined) => { imodelMock - .setup((x) => x.withPreparedStatement(moq.It.isAnyString(), moq.It.isAny())) - .callback((_q, cb) => { - const valueMock = moq.Mock.ofType(); - valueMock.setup((x) => x.getClassNameForClassId()).returns(() => key.className); - const stmtMock = moq.Mock.ofType(); - stmtMock.setup((x) => x.step()).returns(() => DbResult.BE_SQLITE_ROW); - stmtMock.setup((x) => x.getValue(0)).returns(() => valueMock.object); - cb(stmtMock.object); - }); - }; - - const setupIModelForNoResultStatement = (imodelMock: moq.IMock) => { - imodelMock - .setup((x) => x.withPreparedStatement(moq.It.isAnyString(), moq.It.isAny())) - .callback((_q, cb) => { - const stmtMock = moq.Mock.ofType(); - stmtMock.setup((x) => x.step()).returns(() => DbResult.BE_SQLITE_DONE); - cb(stmtMock.object); - }); + .setup((x) => x.elements) + .returns( + () => + ({ + tryGetElementProps: () => (key ? { classFullName: key.className } : undefined), + }) as unknown as IModelDb.Elements, + ); }; describe("constructor", () => { @@ -752,6 +740,7 @@ describe("PresentationManager", () => { const nativePlatformMock = moq.Mock.ofType(); const imodelMock = moq.Mock.ofType(); let manager: PresentationManager; + beforeEach(async () => { testData = { rulesetOrId: await createRandomRuleset(), @@ -767,6 +756,7 @@ describe("PresentationManager", () => { nativePlatformMock.setup((x) => x.getImodelAddon(imodelMock.object)).verifiable(moq.Times.atLeastOnce()); recreateManager(); }); + afterEach(() => { manager[Symbol.dispose](); nativePlatformMock.verifyAll(); @@ -1735,7 +1725,7 @@ describe("PresentationManager", () => { it("returns content set for BisCore:Element instances when concrete key is not found", async () => { // what the addon receives const baseClassKey = { className: "BisCore:Element", id: "0x123" }; - setupIModelForNoResultStatement(imodelMock); + setupIModelForElementKey(imodelMock, undefined); const expectedParams = { requestId: NativePlatformRequestTypes.GetContentSet, params: { @@ -2075,7 +2065,7 @@ describe("PresentationManager", () => { it("returns content for BisCore:Element instances when concrete key is not found", async () => { // what the addon receives const baseClassKey = { className: "BisCore:Element", id: createRandomId() }; - setupIModelForNoResultStatement(imodelMock); + setupIModelForElementKey(imodelMock, undefined); const expectedParams = { requestId: NativePlatformRequestTypes.GetContent, params: { @@ -2359,6 +2349,7 @@ describe("PresentationManager", () => { it("returns no properties for invalid element id", async () => { // what the addon receives const elementKey = { className: "BisCore:Element", id: "0x123" }; + setupIModelForElementKey(imodelMock, undefined); const expectedContentParams = { requestId: NativePlatformRequestTypes.GetContent, @@ -2526,7 +2517,10 @@ describe("PresentationManager", () => { }); function setupIModelForBatchedElementIdsQuery(imodel: moq.IMock, ids: Id64String[]) { - imodel.setup((x) => x.withPreparedStatement(moq.It.isAnyString(), moq.It.isAny())).returns(() => ids.length); + imodel + .setup((x) => x.createQueryReader(moq.It.is((query) => query.trimStart().startsWith("SELECT COUNT(e.ECInstanceId)")))) + .returns(() => stubECSqlReader([{ elementCount: ids.length }])); + imodel .setup((x) => x.createQueryReader(moq.It.is((query) => query.startsWith("SELECT IdToHex(ECInstanceId)")))) .returns(() => stubECSqlReader(ids.map((id) => ({ id })))); diff --git a/presentation/backend/src/test/RulesetEmbedder.test.ts b/presentation/backend/src/test/RulesetEmbedder.test.ts index f4e76dced747..2841ea8e6933 100644 --- a/presentation/backend/src/test/RulesetEmbedder.test.ts +++ b/presentation/backend/src/test/RulesetEmbedder.test.ts @@ -12,13 +12,12 @@ import { DefinitionElement, DefinitionModel, DefinitionPartition, - ECSqlStatement, IModelDb, KnownLocations, Model, Subject, } from "@itwin/core-backend"; -import { DbResult, Id64String } from "@itwin/core-bentley"; +import { Id64String } from "@itwin/core-bentley"; import { BisCodeSpec, Code, @@ -37,6 +36,7 @@ import { PresentationRules } from "../presentation-backend/domain/PresentationRu import * as RulesetElements from "../presentation-backend/domain/RulesetElements"; import { RulesetEmbedder } from "../presentation-backend/RulesetEmbedder"; import { normalizeVersion } from "../presentation-backend/Utils"; +import { stubECSqlReader } from "./Helpers"; describe("RulesetEmbedder", () => { let embedder: RulesetEmbedder; @@ -511,22 +511,12 @@ describe("RulesetEmbedder", () => { describe("getRulesets", () => { function setupMocksForQueryingAllRulesets(rulesets: Array<{ ruleset: Ruleset; elementId: Id64String }>) { - imodelMock - .setup((x) => x.withPreparedStatement(moq.It.isAny(), moq.It.isAny())) - .callback((_ecsql, callbackFun) => { - const statementMock = moq.Mock.ofType(); - rulesets.forEach((entry) => { - statementMock.setup((x) => x.step()).returns(() => DbResult.BE_SQLITE_ROW); - statementMock.setup((x) => x.getRow()).returns(() => ({ id: entry.elementId })); - - const rulesetElementMock = moq.Mock.ofType(); - rulesetElementMock.setup((x) => x.jsonProperties).returns(() => ({ jsonProperties: entry.ruleset })); - elementsMock.setup((x) => x.getElement({ id: entry.elementId })).returns(() => rulesetElementMock.object); - }); - statementMock.setup((x) => x.step()).returns(() => DbResult.BE_SQLITE_DONE); - callbackFun(statementMock.object); - }) - .returns(() => ({})); + rulesets.forEach((entry) => { + const rulesetElementMock = moq.Mock.ofType(); + rulesetElementMock.setup((x) => x.jsonProperties).returns(() => ({ jsonProperties: entry.ruleset })); + elementsMock.setup((x) => x.getElement({ id: entry.elementId })).returns(() => rulesetElementMock.object); + }); + imodelMock.setup((x) => x.createQueryReader(moq.It.isAnyString())).returns(() => stubECSqlReader(rulesets.map((r) => ({ id: r.elementId })))); } it("checks for prerequisites before getting rulesets", async () => { diff --git a/presentation/backend/src/test/SelectionScopesHelper.test.ts b/presentation/backend/src/test/SelectionScopesHelper.test.ts index 24dc78cc8084..c4844aab69cb 100644 --- a/presentation/backend/src/test/SelectionScopesHelper.test.ts +++ b/presentation/backend/src/test/SelectionScopesHelper.test.ts @@ -5,12 +5,13 @@ import { expect } from "chai"; import * as faker from "faker"; import * as moq from "typemoq"; -import { DrawingGraphic, ECSqlStatement, ECSqlValue, Element, IModelDb } from "@itwin/core-backend"; -import { DbResult, Id64, Id64String } from "@itwin/core-bentley"; -import { ElementProps, EntityMetaData, GeometricElement2dProps, IModelError, ModelProps } from "@itwin/core-common"; +import { DrawingGraphic, Element, IModelDb } from "@itwin/core-backend"; +import { Id64, Id64String } from "@itwin/core-bentley"; +import { ElementProps, EntityMetaData, GeometricElement2dProps, ModelProps } from "@itwin/core-common"; import { InstanceKey } from "@itwin/presentation-common"; import { createRandomECInstanceKey, createRandomId } from "@itwin/presentation-common/lib/cjs/test"; import { SelectionScopesHelper } from "../presentation-backend/SelectionScopesHelper"; +import { stubECSqlReader } from "./Helpers"; describe("SelectionScopesHelper", () => { describe("getSelectionScopes", () => { @@ -25,53 +26,6 @@ describe("SelectionScopesHelper", () => { const elementsMock = moq.Mock.ofType(); const modelsMock = moq.Mock.ofType(); - const setupIModelForElementKey = (key: InstanceKey) => { - // this mock simulates the element key query returning a single row with results for the given key (`getElementKey` in Utils.ts) - imodelMock - .setup((x) => - x.withPreparedStatement( - moq.It.is((q) => typeof q === "string" && q.includes("SELECT ECClassId FROM")), - moq.It.isAny(), - ), - ) - .callback((_q, cb) => { - const valueMock = moq.Mock.ofType(); - valueMock.setup((x) => x.getClassNameForClassId()).returns(() => key.className); - const stmtMock = moq.Mock.ofType(); - stmtMock.setup((x) => x.step()).returns(() => DbResult.BE_SQLITE_ROW); - stmtMock.setup((x) => x.getValue(0)).returns(() => valueMock.object); - cb(stmtMock.object); - }); - }; - - const setupIModelForInvalidId = () => { - // this mock simulates trying to bind an invalid id to the element key query (`getElementKey` in Utils.ts) - imodelMock - .setup((x) => - x.withPreparedStatement( - moq.It.is((q) => typeof q === "string" && q.includes("SELECT ECClassId FROM")), - moq.It.isAny(), - ), - ) - .callback((_q, cb) => { - const stmtMock = moq.Mock.ofType(); - stmtMock.setup((x) => x.bindId(moq.It.isAnyNumber(), moq.It.isAny())).throws(new IModelError(DbResult.BE_SQLITE_ERROR, "Error binding Id")); - stmtMock.setup((x) => x.step()).returns(() => DbResult.BE_SQLITE_ERROR); - cb(stmtMock.object); - }); - }; - - const setupIModelForNoResultStatement = () => { - // this mock simulates any kind of query returning no results - imodelMock - .setup((x) => x.withPreparedStatement(moq.It.isAnyString(), moq.It.isAny())) - .callback((_q, cb) => { - const stmtMock = moq.Mock.ofType(); - stmtMock.setup((x) => x.step()).returns(() => DbResult.BE_SQLITE_DONE); - cb(stmtMock.object); - }); - }; - const createRandomModelProps = (): ModelProps => { const id = createRandomId(); const props: ModelProps = { @@ -107,31 +61,32 @@ describe("SelectionScopesHelper", () => { const createTransientElementId = () => Id64.fromLocalAndBriefcaseIds(faker.random.number(), 0xffffff); - const setupIModelForFunctionalKeyQuery = (props: { graphicalElementKey: InstanceKey; stepResult?: DbResult; functionalElementKey?: InstanceKey }) => { + const setupIModelForFunctionalKeyQuery = (props: { graphicalElementKey: InstanceKey; functionalElementKey?: InstanceKey }) => { const functionalKeyQueryIdentifier = "SELECT funcSchemaDef.Name || '.' || funcClassDef.Name funcElClassName, fe.ECInstanceId funcElId"; imodelMock .setup((x) => - x.withPreparedStatement( + x.createQueryReader( moq.It.is((q) => typeof q === "string" && q.includes(functionalKeyQueryIdentifier)), moq.It.isAny(), ), ) - .returns((_q, cb) => { - const stmtMock = moq.Mock.ofType(); - stmtMock.setup((x) => x.step()).returns(() => props.stepResult ?? DbResult.BE_SQLITE_ROW); - stmtMock - .setup((x) => x.getRow()) - .returns(() => ({ + .returns(() => + stubECSqlReader([ + { funcElClassName: props.functionalElementKey?.className, funcElId: props.functionalElementKey?.id, - })); - return cb(stmtMock.object); - }); + }, + ]), + ); }; - const setupIModelForElementProps = (props?: { key?: InstanceKey; parentKey?: InstanceKey; isRemoved?: boolean }) => { + const setupIModelForElementProps = (props?: { key?: InstanceKey; parentKey?: InstanceKey }) => { const key = props?.key ?? createRandomECInstanceKey(); - const elementProps = props?.isRemoved ? undefined : props?.parentKey ? createRandomElementProps(props.parentKey.id) : createRandomTopmostElementProps(); + const elementProps = { + ...(props?.parentKey ? createRandomElementProps(props.parentKey.id) : createRandomTopmostElementProps()), + classFullName: key.className, + id: key.id, + }; elementsMock.setup((x) => x.tryGetElementProps(key.id)).returns(() => elementProps); return { key, props: elementProps }; }; @@ -140,16 +95,12 @@ describe("SelectionScopesHelper", () => { const classDerivesFromQueryIdentifier = "SELECT 1"; imodelMock .setup((x) => - x.withPreparedStatement( + x.createQueryReader( moq.It.is((q) => typeof q === "string" && q.includes(classDerivesFromQueryIdentifier)), moq.It.isAny(), ), ) - .returns((_q, cb) => { - const stmtMock = moq.Mock.ofType(); - stmtMock.setup((x) => x.step()).returns(() => (doesDeriveFromSuppliedClass ? DbResult.BE_SQLITE_ROW : DbResult.BE_SQLITE_DONE)); - return cb(stmtMock.object); - }); + .returns(() => stubECSqlReader(doesDeriveFromSuppliedClass ? [{}] : [])); }; beforeEach(() => { @@ -178,7 +129,7 @@ describe("SelectionScopesHelper", () => { describe("scope: 'element'", () => { it("returns element keys", async () => { const keys = [createRandomECInstanceKey(), createRandomECInstanceKey()]; - keys.forEach((key) => setupIModelForElementKey(key)); + keys.forEach((key) => setupIModelForElementProps({ key })); const result = await SelectionScopesHelper.computeSelection( { imodel: imodelMock.object }, @@ -191,8 +142,6 @@ describe("SelectionScopesHelper", () => { it("skips non-existing element ids", async () => { const keys = [createRandomECInstanceKey()]; - setupIModelForNoResultStatement(); - const result = await SelectionScopesHelper.computeSelection( { imodel: imodelMock.object }, keys.map((k) => k.id), @@ -203,7 +152,7 @@ describe("SelectionScopesHelper", () => { it("skips transient element ids", async () => { const keys = [createRandomECInstanceKey(), { className: "any:class", id: createTransientElementId() }]; - setupIModelForElementKey(keys[0]); + setupIModelForElementProps({ key: keys[0] }); const result = await SelectionScopesHelper.computeSelection( { imodel: imodelMock.object }, @@ -216,9 +165,8 @@ describe("SelectionScopesHelper", () => { it("handles invalid id", async () => { const validKeys = [createRandomECInstanceKey(), createRandomECInstanceKey()]; - setupIModelForElementKey(validKeys[0]); - setupIModelForInvalidId(); - setupIModelForElementKey(validKeys[1]); + setupIModelForElementProps({ key: validKeys[0] }); + setupIModelForElementProps({ key: validKeys[1] }); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [validKeys[0].id, "not an id", validKeys[1].id], "element"); expect(result.size).to.eq(2); @@ -230,7 +178,7 @@ describe("SelectionScopesHelper", () => { const parent2 = setupIModelForElementProps({ key: createRandomECInstanceKey(), parentKey: parent3.key }); const parent1 = setupIModelForElementProps({ key: createRandomECInstanceKey(), parentKey: parent2.key }); const element = setupIModelForElementProps({ key: createRandomECInstanceKey(), parentKey: parent1.key }); - setupIModelForElementKey(parent2.key); + setupIModelForElementProps({ key: parent2.key }); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object, @@ -245,14 +193,11 @@ describe("SelectionScopesHelper", () => { describe("scope: 'assembly'", () => { it("returns parent keys", async () => { const parentKeys = [createRandomECInstanceKey(), createRandomECInstanceKey()]; - parentKeys.forEach((key) => setupIModelForElementKey(key)); - const elementProps = parentKeys.map((pk) => createRandomElementProps(pk.id)); - elementProps.forEach((p) => { - elementsMock.setup((x) => x.tryGetElementProps(p.id!)).returns(() => p); - }); + parentKeys.forEach((key) => setupIModelForElementProps({ key })); + const elementKeys = parentKeys.map((pk) => setupIModelForElementProps({ parentKey: pk }).key); const result = await SelectionScopesHelper.computeSelection( { imodel: imodelMock.object }, - elementProps.map((p) => p.id!), + elementKeys.map(({ id }) => id), "assembly", ); expect(result.size).to.eq(2); @@ -260,15 +205,11 @@ describe("SelectionScopesHelper", () => { }); it("does not duplicate keys", async () => { - const parentKey = createRandomECInstanceKey(); - setupIModelForElementKey(parentKey); - const elementProps = [createRandomElementProps(parentKey.id), createRandomElementProps(parentKey.id)]; - elementProps.forEach((p) => { - elementsMock.setup((x) => x.tryGetElementProps(p.id!)).returns(() => p); - }); + const { key: parentKey } = setupIModelForElementProps(); + const elementKeys = [setupIModelForElementProps({ parentKey }).key, setupIModelForElementProps({ parentKey }).key]; const result = await SelectionScopesHelper.computeSelection( { imodel: imodelMock.object }, - elementProps.map((p) => p.id!), + elementKeys.map(({ id }) => id), "assembly", ); expect(result.size).to.eq(1); @@ -277,77 +218,41 @@ describe("SelectionScopesHelper", () => { it("returns element key if it has no parent", async () => { const key = createRandomECInstanceKey(); - setupIModelForElementKey(key); - const elementProps = createRandomTopmostElementProps(); - elementsMock.setup((x) => x.tryGetElementProps(key.id)).returns(() => elementProps); + setupIModelForElementProps({ key }); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [key.id], "assembly"); expect(result.size).to.eq(1); expect(result.has(key)).to.be.true; }); - it("skips removed elements", async () => { - // set up one existing element with parent - const parentKey = createRandomECInstanceKey(); - setupIModelForElementKey(parentKey); - const existingElementProps = createRandomElementProps(parentKey.id); - elementsMock.setup((x) => x.tryGetElementProps(existingElementProps.id!)).returns(() => existingElementProps); - // set up removed element props - const removedElementProps = createRandomElementProps(); - elementsMock.setup((x) => x.tryGetElementProps(removedElementProps.id!)).returns(() => undefined); - setupIModelForNoResultStatement(); - // request - const result = await SelectionScopesHelper.computeSelection( - { imodel: imodelMock.object }, - [existingElementProps.id!, removedElementProps.id!], - "assembly", - ); - expect(result.size).to.eq(1); - expect(result.has(parentKey)).to.be.true; - }); - it("skips non-existing element ids", async () => { const key = createRandomECInstanceKey(); - setupIModelForNoResultStatement(); - const elementProps = createRandomTopmostElementProps(); - elementsMock.setup((x) => x.tryGetElementProps(key.id)).returns(() => elementProps); + elementsMock.setup((x) => x.tryGetElementProps(key.id)).returns(() => undefined); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [key.id], "assembly"); expect(result.size).to.eq(0); }); it("skips transient element ids", async () => { - const parentKeys = [createRandomECInstanceKey()]; - setupIModelForElementKey(parentKeys[0]); - const elementProps = [createRandomElementProps(parentKeys[0].id)]; - elementsMock.setup((x) => x.tryGetElementProps(elementProps[0].id!)).returns(() => elementProps[0]); - const ids = [elementProps[0].id!, createTransientElementId()]; + const { key: parentKey } = setupIModelForElementProps(); + const { key: elementKey } = setupIModelForElementProps({ parentKey }); + const ids = [elementKey.id, createTransientElementId()]; const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, ids, "assembly"); expect(result.size).to.eq(1); - parentKeys.forEach((key) => expect(result.has(key)).to.be.true); + expect(result.has(parentKey)).to.be.true; }); }); describe("scope: 'top-assembly'", () => { it("returns topmost parent key", async () => { - const grandparent = createRandomTopmostElementProps(); - const grandparentKey = createRandomECInstanceKey(); - setupIModelForElementKey(grandparentKey); - elementsMock.setup((x) => x.tryGetElementProps(grandparentKey.id)).returns(() => grandparent); - const parent = createRandomElementProps(grandparentKey.id); - const parentKey = createRandomECInstanceKey(); - elementsMock.setup((x) => x.tryGetElementProps(parentKey.id)).returns(() => parent); - const element = createRandomElementProps(parentKey.id); - elementsMock.setup((x) => x.tryGetElementProps(element.id!)).returns(() => element); - - const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [element.id!], "top-assembly"); + const { key: grandparentKey } = setupIModelForElementProps(); + const { key: parentKey } = setupIModelForElementProps({ parentKey: grandparentKey }); + const { key: elementKey } = setupIModelForElementProps({ parentKey }); + const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [elementKey.id], "top-assembly"); expect(result.size).to.eq(1); expect(result.has(grandparentKey)).to.be.true; }); it("returns element key if it has no parent", async () => { - const key = createRandomECInstanceKey(); - setupIModelForElementKey(key); - const elementProps = createRandomTopmostElementProps(); - elementsMock.setup((x) => x.tryGetElementProps(key.id)).returns(() => elementProps); + const { key } = setupIModelForElementProps(); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [key.id], "top-assembly"); expect(result.size).to.eq(1); expect(result.has(key)).to.be.true; @@ -355,21 +260,15 @@ describe("SelectionScopesHelper", () => { it("skips non-existing element ids", async () => { const key = createRandomECInstanceKey(); - setupIModelForNoResultStatement(); - const elementProps = createRandomTopmostElementProps(); - elementsMock.setup((x) => x.tryGetElementProps(key.id)).returns(() => elementProps); + elementsMock.setup((x) => x.tryGetElementProps(key.id)).returns(() => undefined); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [key.id], "top-assembly"); expect(result.size).to.eq(0); }); it("skips transient element ids", async () => { - const parent = createRandomTopmostElementProps(); - const parentKey = createRandomECInstanceKey(); - setupIModelForElementKey(parentKey); - elementsMock.setup((x) => x.tryGetElementProps(parentKey.id)).returns(() => parent); - const elementProps = createRandomElementProps(parentKey.id); - elementsMock.setup((x) => x.tryGetElementProps(elementProps.id!)).returns(() => elementProps); - const ids = [elementProps.id!, createTransientElementId()]; + const { key: parentKey } = setupIModelForElementProps(); + const { key: elementKey } = setupIModelForElementProps({ parentKey }); + const ids = [elementKey.id, createTransientElementId()]; const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, ids, "top-assembly"); expect(result.size).to.eq(1); expect(result.has(parentKey)).to.be.true; @@ -513,10 +412,9 @@ describe("SelectionScopesHelper", () => { describe("scope: 'functional-element'", () => { it("returns GeometricElement3d key if it doesn't have an associated functional element or parent", async () => { - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(true); + const { key: graphicalElementKey } = setupIModelForElementProps(); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementKey(graphicalElementKey); + setupIModelDerivesFromClassQuery(true); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional"); expect(result.size).to.eq(1); @@ -526,8 +424,8 @@ describe("SelectionScopesHelper", () => { it("returns functional element key if GeometricElement3d has an associated functional element", async () => { const functionalElementKey = createRandomECInstanceKey(); const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(true); setupIModelForFunctionalKeyQuery({ graphicalElementKey, functionalElementKey }); + setupIModelDerivesFromClassQuery(true); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-element"); expect(result.size).to.eq(1); @@ -535,11 +433,9 @@ describe("SelectionScopesHelper", () => { }); it("returns GeometricElement2d key if it doesn't have an associated functional element or parent", async () => { - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); + const { key: graphicalElementKey } = setupIModelForElementProps(); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementProps({ key: graphicalElementKey }); - setupIModelForElementKey(graphicalElementKey); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional"); expect(result.size).to.eq(1); @@ -547,17 +443,13 @@ describe("SelectionScopesHelper", () => { }); it("returns GeometricElement2d key if it has parents but none of them have related functional elements", async () => { - const graphicalGrandParentElementKey = createRandomECInstanceKey(); - const graphicalParentElementKey = createRandomECInstanceKey(); - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); + const { key: graphicalGrandParentElementKey } = setupIModelForElementProps(); + const { key: graphicalParentElementKey } = setupIModelForElementProps({ parentKey: graphicalGrandParentElementKey }); + const { key: graphicalElementKey } = setupIModelForElementProps({ parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalParentElementKey }); - setupIModelForElementProps({ key: graphicalParentElementKey, parentKey: graphicalGrandParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalGrandParentElementKey }); - setupIModelForElementProps({ key: graphicalGrandParentElementKey }); - setupIModelForElementKey(graphicalElementKey); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-element"); expect(result.size).to.eq(1); @@ -567,8 +459,8 @@ describe("SelectionScopesHelper", () => { it("returns functional element key if GeometricElement2d has an associated functional element", async () => { const functionalElementKey = createRandomECInstanceKey(); const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); setupIModelForFunctionalKeyQuery({ graphicalElementKey, functionalElementKey }); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-element"); expect(result.size).to.eq(1); @@ -577,15 +469,13 @@ describe("SelectionScopesHelper", () => { it("returns functional element key of the first GeometricElement2d parent that has related functional element", async () => { const functionalElementKey = createRandomECInstanceKey(); - const graphicalGrandParentElementKey = createRandomECInstanceKey(); - const graphicalParentElementKey = createRandomECInstanceKey(); - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); + const { key: graphicalGrandParentElementKey } = setupIModelForElementProps(); + const { key: graphicalParentElementKey } = setupIModelForElementProps({ parentKey: graphicalGrandParentElementKey }); + const { key: graphicalElementKey } = setupIModelForElementProps({ parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalParentElementKey }); - setupIModelForElementProps({ key: graphicalParentElementKey, parentKey: graphicalGrandParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalGrandParentElementKey, functionalElementKey }); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-element"); expect(result.size).to.eq(1); @@ -593,11 +483,9 @@ describe("SelectionScopesHelper", () => { }); it("skips transient element ids", async () => { - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); + const { key: graphicalElementKey } = setupIModelForElementProps(); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementProps({ key: graphicalElementKey }); - setupIModelForElementKey(graphicalElementKey); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection( { imodel: imodelMock.object }, @@ -607,41 +495,13 @@ describe("SelectionScopesHelper", () => { expect(result.size).to.eq(1); expect(result.has(graphicalElementKey)).to.be.true; }); - - it("skips removed GeometricElement2d parents when looking for closest functional element", async () => { - setupIModelDerivesFromClassQuery(false); - - // set up one element with existing parent that has a related functional element - const functionalElement = setupIModelForElementProps({ key: createRandomECInstanceKey() }); - const existingParent = setupIModelForElementProps({ key: createRandomECInstanceKey() }); - const existingElement = setupIModelForElementProps({ key: createRandomECInstanceKey(), parentKey: existingParent.key }); - setupIModelForFunctionalKeyQuery({ graphicalElementKey: existingElement.key }); - setupIModelForFunctionalKeyQuery({ graphicalElementKey: existingParent.key, functionalElementKey: functionalElement.key }); - - // set up one element with removed parent - const removedParent = setupIModelForElementProps({ key: createRandomECInstanceKey(), isRemoved: true }); - const elementWithRemovedParent = setupIModelForElementProps({ key: createRandomECInstanceKey(), parentKey: removedParent.key }); - setupIModelForFunctionalKeyQuery({ graphicalElementKey: elementWithRemovedParent.key }); - setupIModelForElementKey(elementWithRemovedParent.key); - - // request - const result = await SelectionScopesHelper.computeSelection( - { imodel: imodelMock.object }, - [existingElement.key.id, elementWithRemovedParent.key.id], - "functional-element", - ); - expect(result.size).to.eq(2); - expect(result.has(functionalElement.key)).to.be.true; - expect(result.has(elementWithRemovedParent.key)).to.be.true; - }); }); describe("scope: 'functional-assembly'", () => { it("returns GeometricElement3d key if it doesn't have a parent", async () => { - const graphicalElementKey = createRandomECInstanceKey(); + const { key: graphicalElementKey } = setupIModelForElementProps(); + setupIModelForFunctionalKeyQuery({ graphicalElementKey }); setupIModelDerivesFromClassQuery(true); - setupIModelForElementProps({ key: graphicalElementKey }); - setupIModelForElementKey(graphicalElementKey); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-assembly"); expect(result.size).to.eq(1); @@ -649,12 +509,10 @@ describe("SelectionScopesHelper", () => { }); it("returns GeometricElement3d parent key if it doesn't have a related functional element", async () => { - const graphicalParentElementKey = createRandomECInstanceKey(); - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(true); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); - setupIModelForElementKey(graphicalParentElementKey); + const { key: graphicalParentElementKey } = setupIModelForElementProps(); + const { key: graphicalElementKey } = setupIModelForElementProps({ parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalParentElementKey }); + setupIModelDerivesFromClassQuery(true); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-assembly"); expect(result.size).to.eq(1); @@ -663,13 +521,11 @@ describe("SelectionScopesHelper", () => { it("returns functional element key of GeometricElement3d parent", async () => { const functionalElementKey = createRandomECInstanceKey(); - const graphicalGrandParentElementKey = createRandomECInstanceKey(); - const graphicalParentElementKey = createRandomECInstanceKey(); - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(true); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); - setupIModelForElementKey(graphicalParentElementKey); + const { key: graphicalGrandParentElementKey } = setupIModelForElementProps(); + const { key: graphicalParentElementKey } = setupIModelForElementProps({ parentKey: graphicalGrandParentElementKey }); + const { key: graphicalElementKey } = setupIModelForElementProps({ parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalGrandParentElementKey, functionalElementKey }); + setupIModelDerivesFromClassQuery(true); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-assembly"); expect(result.size).to.eq(1); @@ -677,11 +533,9 @@ describe("SelectionScopesHelper", () => { }); it("returns GeometricElement2d key if it doesn't have an associated functional element or parent", async () => { - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); + const { key: graphicalElementKey } = setupIModelForElementProps(); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementProps({ key: graphicalElementKey }); - setupIModelForElementKey(graphicalElementKey); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-assembly"); expect(result.size).to.eq(1); @@ -689,18 +543,13 @@ describe("SelectionScopesHelper", () => { }); it("returns first GeometricElement2d parent key if none of the parents have an associated functional element", async () => { - const graphicalGrandParentElementKey = createRandomECInstanceKey(); - const graphicalParentElementKey = createRandomECInstanceKey(); - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); + const { key: graphicalGrandParentElementKey } = setupIModelForElementProps(); + const { key: graphicalParentElementKey } = setupIModelForElementProps({ parentKey: graphicalGrandParentElementKey }); + const { key: graphicalElementKey } = setupIModelForElementProps({ parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalParentElementKey }); - setupIModelForElementProps({ key: graphicalParentElementKey, parentKey: graphicalGrandParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalGrandParentElementKey }); - setupIModelForElementProps({ key: graphicalGrandParentElementKey }); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); - setupIModelForElementKey(graphicalParentElementKey); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-assembly"); expect(result.size).to.eq(1); @@ -708,18 +557,14 @@ describe("SelectionScopesHelper", () => { }); it("returns functional element key of the first GeometricElement2d parent that has a related functional element and the functional element has no parent", async () => { - const functionalElementKey = createRandomECInstanceKey(); - const graphicalGrandParentElementKey = createRandomECInstanceKey(); - const graphicalParentElementKey = createRandomECInstanceKey(); - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); + const { key: graphicalGrandParentElementKey } = setupIModelForElementProps(); + const { key: graphicalParentElementKey } = setupIModelForElementProps({ parentKey: graphicalGrandParentElementKey }); + const { key: graphicalElementKey } = setupIModelForElementProps({ parentKey: graphicalParentElementKey }); + const { key: functionalElementKey } = setupIModelForElementProps(); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalParentElementKey }); - setupIModelForElementProps({ key: graphicalParentElementKey, parentKey: graphicalGrandParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalGrandParentElementKey, functionalElementKey }); - setupIModelForElementProps({ key: functionalElementKey }); - setupIModelForElementKey(functionalElementKey); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-assembly"); expect(result.size).to.eq(1); @@ -727,16 +572,13 @@ describe("SelectionScopesHelper", () => { }); it("returns functional parent element key of the first GeometricElement2d parent that has a related functional element", async () => { - const functionalParentElementKey = createRandomECInstanceKey(); - const functionalElementKey = createRandomECInstanceKey(); - const graphicalParentElementKey = createRandomECInstanceKey(); - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); + const { key: graphicalParentElementKey } = setupIModelForElementProps(); + const { key: graphicalElementKey } = setupIModelForElementProps({ parentKey: graphicalParentElementKey }); + const { key: functionalParentElementKey } = setupIModelForElementProps(); + const { key: functionalElementKey } = setupIModelForElementProps({ parentKey: functionalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalParentElementKey, functionalElementKey }); - setupIModelForElementProps({ key: functionalElementKey, parentKey: functionalParentElementKey }); - setupIModelForElementKey(functionalParentElementKey); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-assembly"); expect(result.size).to.eq(1); @@ -746,10 +588,9 @@ describe("SelectionScopesHelper", () => { describe("scope: 'functional-top-assembly'", () => { it("returns GeometricElement3d key if it doesn't have a parent", async () => { - const graphicalElementKey = createRandomECInstanceKey(); + const { key: graphicalElementKey } = setupIModelForElementProps(); + setupIModelForFunctionalKeyQuery({ graphicalElementKey }); setupIModelDerivesFromClassQuery(true); - setupIModelForElementProps({ key: graphicalElementKey }); - setupIModelForElementKey(graphicalElementKey); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-top-assembly"); expect(result.size).to.eq(1); @@ -757,15 +598,11 @@ describe("SelectionScopesHelper", () => { }); it("returns topmost GeometricElement3d parent key if it doesn't have a related functional element", async () => { - const graphicalGrandParentElementKey = createRandomECInstanceKey(); - const graphicalParentElementKey = createRandomECInstanceKey(); - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(true); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); - setupIModelForElementProps({ key: graphicalParentElementKey, parentKey: graphicalGrandParentElementKey }); - setupIModelForElementProps({ key: graphicalGrandParentElementKey }); - setupIModelForElementKey(graphicalGrandParentElementKey); + const { key: graphicalGrandParentElementKey } = setupIModelForElementProps(); + const { key: graphicalParentElementKey } = setupIModelForElementProps({ parentKey: graphicalGrandParentElementKey }); + const { key: graphicalElementKey } = setupIModelForElementProps({ parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalGrandParentElementKey }); + setupIModelDerivesFromClassQuery(true); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-top-assembly"); expect(result.size).to.eq(1); @@ -774,15 +611,11 @@ describe("SelectionScopesHelper", () => { it("returns functional element key of the topmost GeometricElement3d parent", async () => { const functionalElementKey = createRandomECInstanceKey(); - const graphicalGrandParentElementKey = createRandomECInstanceKey(); - const graphicalParentElementKey = createRandomECInstanceKey(); - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(true); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); - setupIModelForElementProps({ key: graphicalParentElementKey, parentKey: graphicalGrandParentElementKey }); - setupIModelForElementProps({ key: graphicalGrandParentElementKey }); - setupIModelForElementKey(graphicalGrandParentElementKey); + const { key: graphicalGrandParentElementKey } = setupIModelForElementProps(); + const { key: graphicalParentElementKey } = setupIModelForElementProps({ parentKey: graphicalGrandParentElementKey }); + const { key: graphicalElementKey } = setupIModelForElementProps({ parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalGrandParentElementKey, functionalElementKey }); + setupIModelDerivesFromClassQuery(true); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-top-assembly"); expect(result.size).to.eq(1); @@ -790,11 +623,9 @@ describe("SelectionScopesHelper", () => { }); it("returns GeometricElement2d key if it doesn't have an associated functional element or parent", async () => { - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); + const { key: graphicalElementKey } = setupIModelForElementProps(); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementProps({ key: graphicalElementKey }); - setupIModelForElementKey(graphicalElementKey); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-top-assembly"); expect(result.size).to.eq(1); @@ -802,20 +633,13 @@ describe("SelectionScopesHelper", () => { }); it("returns topmost GeometricElement2d parent key if none of the parents have an associated functional element", async () => { - const graphicalGrandParentElementKey = createRandomECInstanceKey(); - const graphicalParentElementKey = createRandomECInstanceKey(); - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); + const { key: graphicalGrandParentElementKey } = setupIModelForElementProps(); + const { key: graphicalParentElementKey } = setupIModelForElementProps({ parentKey: graphicalGrandParentElementKey }); + const { key: graphicalElementKey } = setupIModelForElementProps({ parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalParentElementKey }); - setupIModelForElementProps({ key: graphicalParentElementKey, parentKey: graphicalGrandParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalGrandParentElementKey }); - setupIModelForElementProps({ key: graphicalGrandParentElementKey }); // done looking for functionals - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); - setupIModelForElementProps({ key: graphicalParentElementKey, parentKey: graphicalGrandParentElementKey }); - setupIModelForElementProps({ key: graphicalGrandParentElementKey }); - setupIModelForElementKey(graphicalGrandParentElementKey); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-top-assembly"); expect(result.size).to.eq(1); @@ -823,18 +647,14 @@ describe("SelectionScopesHelper", () => { }); it("returns functional element key of the first GeometricElement2d parent that has a related functional element and the functional element has no parent", async () => { - const functionalElementKey = createRandomECInstanceKey(); - const graphicalGrandParentElementKey = createRandomECInstanceKey(); - const graphicalParentElementKey = createRandomECInstanceKey(); - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); + const { key: graphicalGrandParentElementKey } = setupIModelForElementProps(); + const { key: graphicalParentElementKey } = setupIModelForElementProps({ parentKey: graphicalGrandParentElementKey }); + const { key: graphicalElementKey } = setupIModelForElementProps({ parentKey: graphicalParentElementKey }); + const { key: functionalElementKey } = setupIModelForElementProps(); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalParentElementKey }); - setupIModelForElementProps({ key: graphicalParentElementKey, parentKey: graphicalGrandParentElementKey }); - setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalGrandParentElementKey, functionalElementKey }); // done looking for functionals - setupIModelForElementProps({ key: functionalElementKey }); - setupIModelForElementKey(functionalElementKey); + setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalGrandParentElementKey, functionalElementKey }); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-top-assembly"); expect(result.size).to.eq(1); @@ -842,22 +662,16 @@ describe("SelectionScopesHelper", () => { }); it("returns functional topmost parent element key of the first GeometricElement2d parent that has a related functional element", async () => { - const functionalGrandParentElementKey = createRandomECInstanceKey(); - const functionalParentElementKey = createRandomECInstanceKey(); - const functionalElementKey = createRandomECInstanceKey(); - const graphicalGrandParentElementKey = createRandomECInstanceKey(); - const graphicalParentElementKey = createRandomECInstanceKey(); - const graphicalElementKey = createRandomECInstanceKey(); - setupIModelDerivesFromClassQuery(false); + const { key: graphicalGrandParentElementKey } = setupIModelForElementProps(); + const { key: graphicalParentElementKey } = setupIModelForElementProps({ parentKey: graphicalGrandParentElementKey }); + const { key: graphicalElementKey } = setupIModelForElementProps({ parentKey: graphicalParentElementKey }); + const { key: functionalGrandParentElementKey } = setupIModelForElementProps(); + const { key: functionalParentElementKey } = setupIModelForElementProps({ parentKey: functionalGrandParentElementKey }); + const { key: functionalElementKey } = setupIModelForElementProps({ parentKey: functionalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey }); - setupIModelForElementProps({ key: graphicalElementKey, parentKey: graphicalParentElementKey }); setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalParentElementKey }); - setupIModelForElementProps({ key: graphicalParentElementKey, parentKey: graphicalGrandParentElementKey }); - setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalGrandParentElementKey, functionalElementKey }); // done looking for functionals - setupIModelForElementProps({ key: functionalElementKey, parentKey: functionalParentElementKey }); - setupIModelForElementProps({ key: functionalParentElementKey, parentKey: functionalGrandParentElementKey }); - setupIModelForElementProps({ key: functionalGrandParentElementKey }); - setupIModelForElementKey(functionalGrandParentElementKey); + setupIModelForFunctionalKeyQuery({ graphicalElementKey: graphicalGrandParentElementKey, functionalElementKey }); + setupIModelDerivesFromClassQuery(false); const result = await SelectionScopesHelper.computeSelection({ imodel: imodelMock.object }, [graphicalElementKey.id], "functional-top-assembly"); expect(result.size).to.eq(1); diff --git a/presentation/backend/src/test/Utils.test.ts b/presentation/backend/src/test/Utils.test.ts index 8f3f557770ec..04a3a02bf208 100644 --- a/presentation/backend/src/test/Utils.test.ts +++ b/presentation/backend/src/test/Utils.test.ts @@ -5,58 +5,38 @@ import { expect } from "chai"; import * as sinon from "sinon"; import * as moq from "typemoq"; -import { ECSqlStatement, ECSqlValue, IModelDb } from "@itwin/core-backend"; -import { DbResult } from "@itwin/core-bentley"; +import { IModelDb } from "@itwin/core-backend"; +import { ElementProps } from "@itwin/core-common"; import { createRandomId } from "@itwin/presentation-common/lib/cjs/test"; import { combineDiagnosticsOptions, getElementKey, getLocalizedStringEN, normalizeVersion, reportDiagnostics } from "../presentation-backend/Utils"; describe("getElementKey", () => { const imodel = moq.Mock.ofType(); - const stmt = moq.Mock.ofType(); + let elementResult: { classFullName: string } | undefined; beforeEach(() => { - stmt.reset(); imodel.reset(); imodel - .setup((x) => x.withPreparedStatement(moq.It.isAnyString(), moq.It.isAny())) - .callback((_query: string, cb: (stmt: ECSqlStatement) => void) => { - cb(stmt.object); - }); + .setup((x) => x.elements) + .returns( + () => + ({ + tryGetElementProps: () => elementResult as unknown as ElementProps | undefined, + }) as unknown as IModelDb.Elements, + ); + elementResult = undefined; }); it("returns valid key for existing id", () => { const id = createRandomId(); - - const sqlQueryResult = moq.Mock.ofType(); - sqlQueryResult.setup((x) => x.getClassNameForClassId()).returns(() => "schema.class"); - - stmt.setup((x) => x.bindId(1, id)).verifiable(moq.Times.once()); - stmt - .setup((x) => x.step()) - .returns(() => DbResult.BE_SQLITE_ROW) - .verifiable(moq.Times.once()); - stmt - .setup((x) => x.getValue(0)) - .returns(() => sqlQueryResult.object) - .verifiable(moq.Times.once()); - + elementResult = { classFullName: "schema:class" }; const result = getElementKey(imodel.object, id); - stmt.verifyAll(); - expect(result).to.deep.eq({ className: "schema:class", id }); + expect(result).to.deep.eq({ className: elementResult.classFullName, id }); }); it("returns undefined for non-existing id", () => { const id = "does-not-exist"; - - stmt.setup((x) => x.bindId(1, id)).verifiable(moq.Times.once()); - stmt - .setup((x) => x.step()) - .returns(() => DbResult.BE_SQLITE_DONE) - .verifiable(moq.Times.once()); - stmt.setup((x) => x.getValue(0)).verifiable(moq.Times.never()); - const result = getElementKey(imodel.object, id); - stmt.verifyAll(); expect(result).to.be.undefined; }); }); diff --git a/test-apps/display-test-app/src/backend/SectionDrawingImpl.ts b/test-apps/display-test-app/src/backend/SectionDrawingImpl.ts index 13b3eab9641f..cd3626b99765 100644 --- a/test-apps/display-test-app/src/backend/SectionDrawingImpl.ts +++ b/test-apps/display-test-app/src/backend/SectionDrawingImpl.ts @@ -18,6 +18,7 @@ async function getDrawingProductionListModel(db: BriefcaseDb): Promise { diff --git a/test-apps/export-gltf/src/ExportGltf.ts b/test-apps/export-gltf/src/ExportGltf.ts index 9befdc58b5e5..c0f7ecec6951 100644 --- a/test-apps/export-gltf/src/ExportGltf.ts +++ b/test-apps/export-gltf/src/ExportGltf.ts @@ -438,6 +438,7 @@ const exportGltfArgs = yargs const elementIdArray: Id64Array = []; // Get all 3D elements that aren't part of template definitions or in private models. const sql = "SELECT e.ECInstanceId FROM bis.GeometricElement3d e JOIN bis.Model m ON e.Model.Id=m.ECInstanceId WHERE m.isTemplate=false AND m.isPrivate=false"; + // eslint-disable-next-line @typescript-eslint/no-deprecated GltfGlobals.iModel.withPreparedStatement(sql, (stmt: ECSqlStatement) => { while (stmt.step() === DbResult.BE_SQLITE_ROW) elementIdArray.push(stmt.getValue(0).getId());