import sql, { type config as SqlConfig, type ConnectionPool, type IResult, type Request } from "mssql"; import type { ServerConfig } from "./config.js"; import { quoteIdentifier, resolveDatabaseName, type SqlParam } from "./sql.js"; export type QueryResult = IResult>; export class SqlServerClient { private readonly pools = new Map>(); constructor(private readonly config: ServerConfig) {} get allowedDatabases(): string[] { return this.config.allowedDatabases; } get defaultDatabase(): string { return this.config.defaultDatabase; } get readOnly(): boolean { return this.config.readOnly; } get enableDdl(): boolean { return this.config.enableDdl; } get maxRows(): number { return this.config.maxRows; } get maxWriteRows(): number { return this.config.maxWriteRows; } async query( databaseName: string | undefined, statement: string, params: SqlParam[] = [] ): Promise { const pool = await this.getPool(databaseName); const request = pool.request(); bindParams(request, params); return request.query(statement) as Promise; } async batch(databaseName: string | undefined, statement: string): Promise { const pool = await this.getPool(databaseName); return pool.request().batch(statement) as Promise; } async close(): Promise { const pools = await Promise.all([...this.pools.values()]); await Promise.all(pools.map((pool) => pool.close())); this.pools.clear(); } private async getPool(requestedDatabase: string | undefined): Promise { const database = resolveDatabaseName( requestedDatabase, this.config.defaultDatabase, this.config.allowedDatabases ); const key = database.toLowerCase(); const existing = this.pools.get(key); if (existing) { return existing; } const poolPromise = new sql.ConnectionPool(buildSqlConfig(this.config, database)).connect(); this.pools.set(key, poolPromise); return poolPromise; } } export function useDatabaseStatement(databaseName: string): string { return `USE ${quoteIdentifier(databaseName)};`; } function buildSqlConfig(config: ServerConfig, database: string): SqlConfig { return { server: config.serverName, database, user: config.user, password: config.password, connectionTimeout: config.connectionTimeoutMs, requestTimeout: config.requestTimeoutMs, options: { encrypt: config.encrypt, trustServerCertificate: config.trustServerCertificate }, pool: { max: 5, min: 0, idleTimeoutMillis: 30000 } }; } function bindParams(request: Request, params: SqlParam[]): void { for (const param of params) { request.input(param.name, param.value); } }