Skip to main content
Glama
dbindexes.ts4.76 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { allOk, badRequest, OperationOutcomeError } from '@medplum/core'; import type { FhirRequest, FhirResponse } from '@medplum/fhir-router'; import type { OperationDefinition } from '@medplum/fhirtypes'; import type { Pool, PoolClient } from 'pg'; import { requireSuperAdmin } from '../../admin/super'; import { DatabaseMode, getDatabasePool } from '../../database'; import { escapeUnicode } from '../../migrations/migrate-utils'; import { isValidTableName, replaceNullWithUndefinedInRows, SqlBuilder } from '../sql'; import { buildOutputParameters, makeOperationDefinitionParameter as param, parseInputParameters, } from './utils/parameters'; const operation: OperationDefinition = { resourceType: 'OperationDefinition', name: 'db-indexes', status: 'active', kind: 'operation', code: 'db-indexes', experimental: true, system: true, type: false, instance: false, parameter: [ param('in', 'tableName', 'string', 0, '*'), param('out', 'defaultGinPendingListLimit', 'integer', 1, '1'), param('out', 'index', undefined, 0, '*', [ param('out', 'schemaName', 'string', 1, '1'), param('out', 'tableName', 'string', 1, '1'), param('out', 'indexName', 'string', 1, '1'), param('out', 'indexOptions', 'string', 0, '1'), param('out', 'fastUpdate', 'boolean', 0, '1'), param('out', 'ginPendingListLimit', 'integer', 0, '1'), ]), ], }; interface GinIndexInfo { schemaName: string; tableName: string; indexName: string; indexOptions?: string; fastUpdate: boolean; ginPendingListLimit?: number; } export async function dbIndexesHandler(req: FhirRequest): Promise<FhirResponse> { requireSuperAdmin(); const params = parseInputParameters<{ tableName?: string }>(operation, req); const tableNames = []; for (const tableName of params.tableName?.split(',').map((name) => name.trim()) ?? []) { if (!isValidTableName(tableName)) { throw new OperationOutcomeError(badRequest('Invalid tableName')); } tableNames.push(tableName); } const defaultGinPendingListLimit = await getDefaultGinPendingListLimit(); const client = getDatabasePool(DatabaseMode.WRITER); let index: GinIndexInfo[] | undefined; if (tableNames.length > 0) { index = await getGinIndexInfo(client, tableNames); } const output: { defaultGinPendingListLimit: number; index?: GinIndexInfo[] } = { defaultGinPendingListLimit, index, }; return [allOk, buildOutputParameters(operation, output)]; } async function getDefaultGinPendingListLimit(): Promise<number> { const client = getDatabasePool(DatabaseMode.WRITER); const defaultStatisticsTarget = await client.query('SELECT setting FROM pg_settings WHERE name = $1', [ 'gin_pending_list_limit', ]); return Number(defaultStatisticsTarget.rows[0].setting); } async function getGinIndexInfo(client: PoolClient | Pool, tableNames: string[]): Promise<GinIndexInfo[]> { const schemaName = 'public'; const builder = new SqlBuilder(); const sql = `SELECT n.nspname as "schemaName", t.relname as "tableName", i.relname as "indexName", i.reloptions as "indexOptions", (SELECT (regexp_matches(coalesce(quote_ident(opt), ''), 'fastupdate=(\\w+)'))[1]::boolean FROM unnest(coalesce(i.reloptions, '{}')) AS opt WHERE opt ~ '^fastupdate=') AS "fastUpdate", (SELECT (regexp_matches(coalesce(quote_ident(opt), ''), 'gin_pending_list_limit=(\\d+)'))[1]::INT FROM unnest(coalesce(i.reloptions, '{}')) AS opt WHERE opt ~ '^gin_pending_list_limit=') AS "ginPendingListLimit" FROM pg_index ix JOIN pg_class i ON ix.indexrelid = i.oid JOIN pg_class t ON ix.indrelid = t.oid JOIN pg_namespace AS n ON n.oid = t.relnamespace JOIN pg_am a ON i.relam = a.oid WHERE a.amname = 'gin' AND n.nspname = `; builder.append(sql); builder.param(schemaName); builder.append(' AND t.relname IN '); builder.appendParameters(tableNames, true); builder.append(' ORDER BY "tableName", "indexName"'); const results = await client.query<{ schemaName: string; tableName: string; indexName: string; indexOptions: string; fastUpdate: boolean; ginPendingListLimit: number; }>(builder.toString(), builder.getValues()); const rows = results.rows as GinIndexInfo[]; replaceNullWithUndefinedInRows(results.rows); for (const row of rows) { if (row.indexOptions) { row.indexOptions = arrayToString(row.indexOptions as unknown as string[]); } } return rows; } function arrayToString(arr: (number | string)[] | undefined): string | undefined { return arr && '{' + escapeUnicode(arr.join(',')) + '}'; }

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