@itwin/presentation-hierarchies
Version:
A package for creating hierarchies based on data in iTwin.js iModels.
124 lines • 5.12 kB
JavaScript
;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createLimitingECSqlQueryExecutor = createLimitingECSqlQueryExecutor;
const core_bentley_1 = require("@itwin/core-bentley");
const presentation_shared_1 = require("@itwin/presentation-shared");
const HierarchyErrors_js_1 = require("../HierarchyErrors.js");
const Common_js_1 = require("../internal/Common.js");
const LoggingUtils_js_1 = require("../internal/LoggingUtils.js");
/**
* Creates an `LimitingECSqlQueryExecutor` that throws `RowsLimitExceededError` if the query exceeds given amount of rows.
* @public
*/
function createLimitingECSqlQueryExecutor(baseExecutor, defaultLimit) {
return {
async *createQueryReader(query, config) {
const { limit: configLimit, ...restConfig } = config ?? {};
const limit = configLimit ?? defaultLimit;
const queryLogger = createQueryLogger(query);
const releaseMainThread = (0, presentation_shared_1.createMainThreadReleaseOnTimePassedHandler)();
// handle "unbounded" case without a buffer
const reader = baseExecutor.createQueryReader({ ...query, ecsql: addLimit(query.ecsql, limit) }, restConfig);
if (limit === "unbounded") {
try {
for await (const row of reader) {
await releaseMainThread();
queryLogger.onStep();
yield row;
}
}
finally {
queryLogger.onComplete();
}
return;
}
// avoid streaming until we know the number of rows is okay
const buffer = [];
try {
for await (const row of reader) {
await releaseMainThread();
queryLogger.onStep();
buffer.push(row);
if (buffer.length > limit) {
throw new HierarchyErrors_js_1.RowsLimitExceededError(limit);
}
}
}
finally {
queryLogger.onComplete();
}
for (const row of buffer) {
await releaseMainThread();
yield row;
}
},
};
}
function addLimit(ecsql, limit) {
if (limit === "unbounded") {
return ecsql;
}
return `
SELECT *
FROM (${ecsql})
LIMIT ${limit + 1}
`;
}
const LOGGING_NAMESPACE = `${Common_js_1.LOGGING_NAMESPACE}.Queries`;
const LOGGING_NAMESPACE_PERFORMANCE = `${Common_js_1.LOGGING_NAMESPACE_PERFORMANCE}.Queries`;
function createQueryLogger(query, firstStepWarningThreshold = 3000, allRowsWarningThreshold = 5000) {
const queryId = core_bentley_1.Guid.createValue();
/* v8 ignore next -- @preserve */
(0, LoggingUtils_js_1.doLog)({
category: LOGGING_NAMESPACE,
message: () => `Executing query [${queryId}]: ${createQueryLogMessage(query)}`,
});
let firstStep = true;
let rowsCount = 0;
const timer = new core_bentley_1.StopWatch(undefined, true);
return {
onStep() {
if (firstStep) {
/* v8 ignore start */
(0, LoggingUtils_js_1.doLog)({
category: LOGGING_NAMESPACE_PERFORMANCE,
severity: timer.current.milliseconds >= firstStepWarningThreshold ? "warning" : "trace",
message: () => `[${queryId}] First step took ${timer.currentSeconds} s.`,
});
/* v8 ignore stop */
firstStep = false;
}
++rowsCount;
},
onComplete() {
/* v8 ignore start */
(0, LoggingUtils_js_1.doLog)({
category: LOGGING_NAMESPACE_PERFORMANCE,
severity: timer.current.milliseconds >= allRowsWarningThreshold ? "warning" : "trace",
message: () => `[${queryId}] Query took ${timer.currentSeconds} s. for ${rowsCount} rows.`,
});
/* v8 ignore stop */
},
};
}
/* v8 ignore start */
function createQueryLogMessage(query) {
const ctes = query.ctes?.map((cte) => ` ${(0, presentation_shared_1.trimWhitespace)(cte)}`).join(", \n");
const bindings = query.bindings?.map((b) => JSON.stringify(b.value)).join(", ");
let output = "{\n";
if (ctes) {
output += ` ctes: [ \n${ctes} \n], \n`;
}
output += ` ecsql: ${(0, presentation_shared_1.trimWhitespace)(query.ecsql)}, \n`;
if (bindings) {
output += ` bindings: [${bindings}], \n`;
}
output += "}";
return output;
}
/* v8 ignore stop */
//# sourceMappingURL=LimitingECSqlQueryExecutor.js.map