Skip to main content
Glama
explain.ts3.51 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { allOk, parseSearchRequest } from '@medplum/core'; import type { FhirRequest, FhirResponse } from '@medplum/fhir-router'; import type { OperationDefinition, Project, Reference } from '@medplum/fhirtypes'; import { requireSuperAdmin } from '../../admin/super'; import { DatabaseMode } from '../../database'; import { escapeUnicode } from '../../migrations/migrate-utils'; import { withLongRunningDatabaseClient } from '../../migrations/migration-utils'; import { getSelectQueryForSearch } from '../search'; import { SqlBuilder } from '../sql'; import { buildOutputParameters, parseInputParameters } from './utils/parameters'; const operation: OperationDefinition = { resourceType: 'OperationDefinition', name: 'db-explain', status: 'active', kind: 'operation', code: 'explain', experimental: true, system: true, type: false, instance: false, parameter: [ { use: 'in', name: 'query', type: 'string', min: 1, max: '1', }, { use: 'in', name: 'analyze', type: 'boolean', min: 0, max: '1', }, { use: 'in', name: 'format', type: 'string', min: 0, max: '1', }, { use: 'out', name: 'query', type: 'string', min: 1, max: '1', }, { use: 'out', name: 'parameters', type: 'string', min: 1, max: '1', }, { use: 'out', name: 'explain', type: 'string', min: 1, max: '1', }, ], }; export async function dbExplainHandler(req: FhirRequest): Promise<FhirResponse> { const { repo } = requireSuperAdmin(); const params = parseInputParameters<{ query: string; project?: Reference<Project>; analyze?: boolean; format?: 'text' | 'json'; }>(operation, req); const searchReq = parseSearchRequest(params.query); const selectQuery = getSelectQueryForSearch(repo, searchReq); // Capture SQL query and parameters before adding EXPLAIN const sqlBuilder = new SqlBuilder(); selectQuery.buildSql(sqlBuilder); const query = sqlBuilder.toString(); const parameters = sqlBuilder .getValues() .map((v, i) => `$${i + 1} = ${formatQueryParam(v)}`) .join(', '); selectQuery.explain = ['settings']; if (params.analyze) { selectQuery.explain.push('analyze', 'buffers'); } if (params.format === 'json') { selectQuery.explain.push('format json'); } const result = await withLongRunningDatabaseClient((client) => selectQuery.execute(client), DatabaseMode.READER); let explain: string; if (params.format === 'json') { explain = result[0]['QUERY PLAN'][0]; explain = JSON.stringify(explain, (key, value) => (key.endsWith('Blocks') && value === 0 ? undefined : value), 0); } else { explain = result.map((r) => r['QUERY PLAN']).join('\n'); } const output = buildOutputParameters(operation, { query, parameters, explain, }); return [allOk, output]; } /** * This is probably an incomplete implementation, but is meant to approximate the output * in auto_explain slow query entries * @param param - The parameter value to format * @returns The formatted parameter value */ function formatQueryParam(param: any): string { if (typeof param === 'number') { return param.toString(); } return `'${typeof param === 'string' ? escapeUnicode(param) : param}'`; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server