Skip to content

Commit aa75201

Browse files
mergify[bot]MichaelSwigerAtBentleyCopilotimodeljs-adminanmolshres98
authored
Add integrityCheck() function (backport #8958) [release/5.6.x] (#8986)
Co-authored-by: Michael Swiger <108895074+MichaelSwigerAtBentley@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: imodeljs-admin <38288322+imodeljs-admin@users.noreply.github.com> Co-authored-by: anmolshres98 <anmolshres98@users.noreply.github.com>
1 parent 07db2ee commit aa75201

File tree

14 files changed

+1169
-11
lines changed

14 files changed

+1169
-11
lines changed

.vscode/cSpell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@
234234
"Linestyle",
235235
"lineweight",
236236
"linework",
237+
"Linktable",
237238
"listboxitem",
238239
"lstyle",
239240
"Manip",

common/api/core-backend.api.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3721,6 +3721,8 @@ export abstract class IModelDb extends IModel {
37213721
protected initializeIModelDb(when?: "pullMerge"): void;
37223722
// @beta
37233723
inlineGeometryParts(): InlineGeometryPartsResult;
3724+
// @beta
3725+
integrityCheck(options?: IntegrityCheckOptions): Promise<IntegrityCheckResult[]>;
37243726
get isBriefcase(): boolean;
37253727
isBriefcaseDb(): this is BriefcaseDb;
37263728
// @internal
@@ -4284,6 +4286,23 @@ export interface InstanceChange {
42844286
summaryId: Id64String;
42854287
}
42864288

4289+
// @beta
4290+
export interface IntegrityCheckOptions {
4291+
quickCheck?: boolean;
4292+
specificChecks?: {
4293+
checkDataColumns?: boolean;
4294+
checkECProfile?: boolean;
4295+
checkNavigationClassIds?: boolean;
4296+
checkNavigationIds?: boolean;
4297+
checkLinktableForeignKeyClassIds?: boolean;
4298+
checkLinktableForeignKeyIds?: boolean;
4299+
checkClassIds?: boolean;
4300+
checkDataSchema?: boolean;
4301+
checkSchemaLoad?: boolean;
4302+
checkMissingChildRows?: boolean;
4303+
};
4304+
}
4305+
42874306
// @public
42884307
export abstract class IpcHandler {
42894308
abstract get channelName(): string;

common/api/summary/core-backend.exports.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ internal;function;initializeTracing
360360
beta;interface;InlineGeometryPartsResult
361361
public;interface;InsertElementOptions
362362
beta;interface;InstanceChange
363+
beta;interface;IntegrityCheckOptions
363364
public;class;IpcHandler
364365
public;class;IpcHost
365366
public;interface;IpcHostOpts
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@itwin/core-backend",
5+
"comment": "",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@itwin/core-backend"
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@itwin/core-backend",
5+
"comment": "Add iModelDb.integrityCheck function that checks imodel database for corruption and reports results",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@itwin/core-backend"
10+
}

common/config/rush/pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
"webpack": "^5.97.1"
114114
},
115115
"dependencies": {
116-
"@bentley/imodeljs-native": "5.6.14",
116+
"@bentley/imodeljs-native": "5.6.15",
117117
"@itwin/object-storage-azure": "^3.0.4",
118118
"@azure/storage-blob": "^12.28.0",
119119
"form-data": "^4.0.4",

core/backend/src/IModelDb.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ import { ECVersion, SchemaContext, SchemaJsonLocater } from "@itwin/ecschema-met
7373
import { SchemaMap } from "./Schema";
7474
import { ElementLRUCache, InstanceKeyLRUCache } from "./internal/ElementLRUCache";
7575
import { IModelIncrementalSchemaLocater } from "./IModelIncrementalSchemaLocater";
76+
import { IntegrityCheckKey, IntegrityCheckResult, integrityCheckTypeMap, performQuickIntegrityCheck, performSpecificIntegrityCheck } from "./internal/IntegrityCheck";
77+
7678
// spell:ignore fontid fontmap
7779

7880
const loggerCategory: string = BackendLoggerCategory.IModelDb;
@@ -130,6 +132,38 @@ export interface ComputedProjectExtents {
130132
outliers?: Id64Array;
131133
}
132134

135+
/**
136+
* Options for performing integrity checks on an iModel.
137+
* @beta
138+
*/
139+
export interface IntegrityCheckOptions {
140+
/** If true, perform a quick integrity check that only reports whether each check passed or failed, without detailed results. */
141+
quickCheck?: boolean;
142+
/** Options for performing specific integrity checks with detailed results. */
143+
specificChecks?: {
144+
/** If true, checks if all the required columns exist in data tables. Issues are returned as a list of those tables/columns. */
145+
checkDataColumns?: boolean;
146+
/** If true, checks if the profile table, indexes, and triggers are present. Does not check be_* tables. Issues are returned as a list of tables/indexes/triggers which were not found or have different DDL. */
147+
checkECProfile?: boolean;
148+
/** If true, checks if RelClassId of a Navigation property is a valid ECClassId. It does not check the value to match the relationship class. */
149+
checkNavigationClassIds?: boolean;
150+
/** If true, checks if Id of a Navigation property matches a valid row primary class. */
151+
checkNavigationIds?: boolean;
152+
/** If true, checks if SourceECClassId or TargetECClassId of a link table matches a valid ECClassId. */
153+
checkLinktableForeignKeyClassIds?: boolean;
154+
/** If true, checks if SourceECInstanceId or TargetECInstanceId of a link table matches a valid row in primary class. */
155+
checkLinktableForeignKeyIds?: boolean;
156+
/** If true, checks persisted ECClassId in all data tables and makes sure they are valid. */
157+
checkClassIds?: boolean;
158+
/** If true, checks if all the required data tables and indexes exist for mapped classes. Issues are returned as a list of tables/columns which were not found or have different DDL. */
159+
checkDataSchema?: boolean;
160+
/** If true, checks if all schemas can be loaded into memory. */
161+
checkSchemaLoad?: boolean;
162+
/** If true, checks if all child rows have a corresponding parent row. */
163+
checkMissingChildRows?: boolean;
164+
}
165+
}
166+
133167
/**
134168
* Options for the importing of schemas
135169
* @public
@@ -611,6 +645,60 @@ export abstract class IModelDb extends IModel {
611645
this[_nativeDb].analyze();
612646
}
613647

648+
/**
649+
* Performs integrity checks on this iModel.
650+
* Types of integrity checks that can be performed are:
651+
*
652+
* Default Check:
653+
* - Quick Check: Runs all integrity checks below and returns whether each check passed or failed, without detailed results.
654+
*
655+
* Specific Checks:
656+
* - Data Columns Check: Checks if all the required columns exist in data tables. Issues are returned as a list of those tables/columns.
657+
* - EC Profile Check: Checks if the profile table, indexes, and triggers are present. Does not check be_* tables. Issues are returned as a list of tables/indexes/triggers which were not found or have different DDL.
658+
* - Navigation Class Ids Check: Checks if RelClassId of a Navigation property is a valid ECClassId. It does not check the value to match the relationship class.
659+
* - Navigation Ids Check: Checks if Id of a Navigation property matches a valid row primary class.
660+
* - Linktable Foreign Key Class Ids Check: Checks if SourceECClassId or TargetECClassId of a link table matches a valid ECClassId.
661+
* - Linktable Foreign Key Ids Check: Checks if SourceECInstanceId or TargetECInstanceId of a link table matches a valid row in primary class.
662+
* - Class Ids Check: Checks persisted ECClassId in all data tables and makes sure they are valid.
663+
* - Data Schema Check: Checks if all the required data tables and indexes exist for mapped classes. Issues are returned as a list of tables/columns which were not found or have different DDL.
664+
* - Schema Load Check: Checks if all schemas can be loaded into memory.
665+
* - Missing Child Rows Check: Checks if all child rows have a corresponding parent row.
666+
*
667+
* @param options Options specifying which integrity checks to perform. If no options are provided or all options are false, a quick check will be performed by default.
668+
* @returns An array of integrity check results.
669+
* @throws [[IModelError]] if the iModel is not open.
670+
* @beta
671+
*/
672+
public async integrityCheck(options?: IntegrityCheckOptions): Promise<IntegrityCheckResult[]> {
673+
if (!this.isOpen)
674+
throw new IModelError(IModelStatus.BadRequest, "IModel is not open");
675+
676+
// Default to quick check if no options provided at all, or if not explicitly set and no specific checks are enabled
677+
if (!options || (!options.quickCheck && (!options.specificChecks || !Object.values(options.specificChecks).some(Boolean)))) {
678+
options = { ...options, quickCheck: true };
679+
}
680+
681+
const integrityCheckResults: IntegrityCheckResult[] = [];
682+
// Perform a quick check if requested
683+
if (options.quickCheck) {
684+
const results = await performQuickIntegrityCheck(this);
685+
const passed = results.every((result) => result.passed);
686+
integrityCheckResults.push({ check: "Quick Check", passed, results });
687+
}
688+
// Perform all specific checks requested
689+
if (options.specificChecks) {
690+
for (const [checkKey, checkParams] of Object.entries(integrityCheckTypeMap)) {
691+
if (options.specificChecks[checkKey as keyof typeof options.specificChecks]) {
692+
const results = await performSpecificIntegrityCheck(this, checkKey as IntegrityCheckKey);
693+
const passed = results.length === 0;
694+
integrityCheckResults.push({ check: checkParams.name, passed, results });
695+
}
696+
}
697+
}
698+
699+
return integrityCheckResults;
700+
}
701+
614702
/** @internal */
615703
public async refreshContainerForRpc(_userAccessToken: AccessToken): Promise<void> { }
616704

0 commit comments

Comments
 (0)