UNPKG

@itwin/presentation-hierarchies

Version:

A package for creating hierarchies based on data in iTwin.js iModels.

124 lines 5.12 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * 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