Skip to main content
Glama

MCP SQL Server

by ryudg
query.service.ts6.97 kB
import { DatabaseConnectionManager } from "../database/connection/connection.manager.js"; import { Logger } from "../core/logger.js"; import { QueryResult, QueryOptions, SqlParameter, } from "../types/database.types.js"; /** * Query Execution Service * * Provides advanced functionality for database query execution. */ export class QueryService { private queryHistory: Array<{ query: string; timestamp: Date; executionTime: number; success: boolean; error?: string; }> = []; constructor( private dbManager: DatabaseConnectionManager, private logger: Logger ) {} /** * Execute single query */ async executeQuery( query: string, options?: QueryOptions ): Promise<QueryResult> { const startTime = Date.now(); try { this.logger.info("Starting query execution", { query: query.substring(0, 100) + (query.length > 100 ? "..." : ""), options, }); const connection = this.dbManager.getCurrentConnection(); const result = await connection.executeQuery(query, options); // Add to query history const executionTime = Date.now() - startTime; this.queryHistory.push({ query, timestamp: new Date(), executionTime, success: true, }); this.logger.logQuery(query, executionTime, true); return result; } catch (error) { const executionTime = Date.now() - startTime; this.logger.error("Query execution failed", { query: query.substring(0, 100) + (query.length > 100 ? "..." : ""), executionTime, error: error instanceof Error ? error.message : String(error), }); // Add to query history this.queryHistory.push({ query, timestamp: new Date(), executionTime, success: false, error: error instanceof Error ? error.message : String(error), }); throw error; } } /** * Execute batch queries */ async executeBatch(queries: string[]): Promise<QueryResult[]> { this.logger.info("Starting batch query execution", { queryCount: queries.length, }); const results: QueryResult[] = []; const startTime = Date.now(); try { for (const query of queries) { const result = await this.executeQuery(query); results.push(result); } const totalExecutionTime = Date.now() - startTime; this.logger.info("Batch query execution completed", { queryCount: queries.length, totalExecutionTime, successCount: results.filter((r) => r.success).length, }); return results; } catch (error) { this.logger.error("Batch query execution failed", { queryCount: queries.length, completedQueries: results.length, error: error instanceof Error ? error.message : String(error), }); throw error; } } /** * Get query execution statistics */ getQueryStats(): any { const totalQueries = this.queryHistory.length; const successfulQueries = this.queryHistory.filter((q) => q.success).length; const failedQueries = totalQueries - successfulQueries; const executionTimes = this.queryHistory.map((q) => q.executionTime); const averageExecutionTime = executionTimes.length > 0 ? Math.round( executionTimes.reduce((sum, time) => sum + time, 0) / executionTimes.length ) : 0; const slowestQuery = this.queryHistory.reduce( (slowest, current) => current.executionTime > slowest.executionTime ? current : slowest, this.queryHistory[0] || { executionTime: 0, query: "", timestamp: new Date(), } ); const fastestQuery = this.queryHistory.reduce( (fastest, current) => current.executionTime < fastest.executionTime ? current : fastest, this.queryHistory[0] || { executionTime: 0, query: "", timestamp: new Date(), } ); return { totalQueries, successfulQueries, failedQueries, successRate: totalQueries > 0 ? Math.round((successfulQueries / totalQueries) * 100) : 0, averageExecutionTime, slowestQuery: { query: slowestQuery.query.substring(0, 100) + (slowestQuery.query.length > 100 ? "..." : ""), executionTime: slowestQuery.executionTime, timestamp: slowestQuery.timestamp, }, fastestQuery: { query: fastestQuery.query.substring(0, 100) + (fastestQuery.query.length > 100 ? "..." : ""), executionTime: fastestQuery.executionTime, timestamp: fastestQuery.timestamp, }, }; } /** * Get query history */ getQueryHistory(limit?: number): any[] { const history = [...this.queryHistory].reverse(); return limit ? history.slice(0, limit) : history; } /** * Clear query history */ clearHistory(): void { this.queryHistory = []; this.logger.info("Query history cleared"); } /** * Execute queries within a transaction */ async executeInTransaction( queries: Array<{ query: string; parameters?: SqlParameter[] }> ): Promise<QueryResult[]> { this.logger.info("Transaction started", { queryCount: queries.length }); const results: QueryResult[] = []; try { // Start transaction await this.executeQuery("BEGIN TRANSACTION"); for (const { query, parameters } of queries) { const result = await this.executeQuery(query, { parameters }); results.push(result); if (!result.success) { throw new Error(`Transaction query failed: ${result.error}`); } } // Commit transaction await this.executeQuery("COMMIT TRANSACTION"); this.logger.info("Transaction completed successfully", { queryCount: queries.length, }); return results; } catch (error) { // Transaction rollback try { await this.executeQuery("ROLLBACK TRANSACTION"); this.logger.info("Transaction rollback completed"); } catch (rollbackError) { this.logger.error("Transaction rollback failed", { rollbackError }); } this.logger.error("Transaction failed", { completedQueries: results.length, error: error instanceof Error ? error.message : String(error), }); throw error; } } /** * Get query execution plan */ async getExecutionPlan(query: string): Promise<QueryResult> { const planQuery = `SET SHOWPLAN_ALL ON\n${query}\nSET SHOWPLAN_ALL OFF`; return this.executeQuery(planQuery); } /** * Test connection */ async testConnection(): Promise<boolean> { try { const result = await this.executeQuery("SELECT 1 as test"); return result.success && result.recordset?.[0]?.test === 1; } catch { return false; } } }

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/ryudg/mcp-sql'

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