@@ -112,7 +112,7 @@ async def _execute_query(self, sql: str, params: Optional[tuple] = None, databas
112112 logger .error ("Connection pool is not initialized." )
113113 raise RuntimeError ("Database connection pool not available." )
114114
115- allowed_prefixes = ('SELECT' , 'SHOW' , 'DESC' , 'DESCRIBE' , 'USE' )
115+ allowed_prefixes = ('SELECT' , 'SHOW' , 'DESC' , 'DESCRIBE' , 'USE' , 'EXPLAIN' )
116116 query_upper = sql .strip ().upper ()
117117 is_allowed_read_query = any (query_upper .startswith (prefix ) for prefix in allowed_prefixes )
118118
@@ -359,6 +359,52 @@ async def create_database(self, database_name: str) -> Dict[str, Any]:
359359 logger .error (f"TOOL ERROR: create_database. { error_message } Error: { e } " , exc_info = True )
360360 raise RuntimeError (f"{ error_message } Reason: { str (e )} " )
361361
362+ async def explain_query (self , sql_query : str , database_name : str , parameters : Optional [List [Any ]] = None ) -> List [Dict [str , Any ]]:
363+ """
364+ Executes EXPLAIN on a SQL query to show the execution plan.
365+ This helps analyze query performance and optimization opportunities.
366+ Example `parameters`: ["value1", 123] corresponding to %s placeholders in `sql_query`.
367+ """
368+ logger .info (f"TOOL START: explain_query called. database_name={ database_name } , sql_query={ sql_query [:100 ]} , parameters={ parameters } " )
369+ if database_name and not database_name .isidentifier ():
370+ logger .warning (f"TOOL WARNING: explain_query called with invalid database_name: { database_name } " )
371+ raise ValueError (f"Invalid database name provided: { database_name } " )
372+
373+ # EXPLAIN 키워드를 쿼리 앞에 추가
374+ explain_sql = f"EXPLAIN { sql_query .strip ()} "
375+ param_tuple = tuple (parameters ) if parameters is not None else None
376+
377+ try :
378+ results = await self ._execute_query (explain_sql , params = param_tuple , database = database_name )
379+ logger .info (f"TOOL END: explain_query completed. Execution plan rows returned: { len (results )} ." )
380+ return results
381+ except Exception as e :
382+ logger .error (f"TOOL ERROR: explain_query failed for database_name={ database_name } , sql_query={ sql_query [:100 ]} , parameters={ parameters } : { e } " , exc_info = True )
383+ raise
384+
385+ async def explain_query_extended (self , sql_query : str , database_name : str , parameters : Optional [List [Any ]] = None ) -> List [Dict [str , Any ]]:
386+ """
387+ Executes EXPLAIN EXTENDED on a SQL query to show detailed execution plan with additional information.
388+ This provides more comprehensive analysis including filtered rows percentage and extra information.
389+ Example `parameters`: ["value1", 123] corresponding to %s placeholders in `sql_query`.
390+ """
391+ logger .info (f"TOOL START: explain_query_extended called. database_name={ database_name } , sql_query={ sql_query [:100 ]} , parameters={ parameters } " )
392+ if database_name and not database_name .isidentifier ():
393+ logger .warning (f"TOOL WARNING: explain_query_extended called with invalid database_name: { database_name } " )
394+ raise ValueError (f"Invalid database name provided: { database_name } " )
395+
396+ # EXPLAIN EXTENDED 키워드를 쿼리 앞에 추가
397+ explain_sql = f"EXPLAIN EXTENDED { sql_query .strip ()} "
398+ param_tuple = tuple (parameters ) if parameters is not None else None
399+
400+ try :
401+ results = await self ._execute_query (explain_sql , params = param_tuple , database = database_name )
402+ logger .info (f"TOOL END: explain_query_extended completed. Extended execution plan rows returned: { len (results )} ." )
403+ return results
404+ except Exception as e :
405+ logger .error (f"TOOL ERROR: explain_query_extended failed for database_name={ database_name } , sql_query={ sql_query [:100 ]} , parameters={ parameters } : { e } " , exc_info = True )
406+ raise
407+
362408 async def create_vector_store_tool (self ,
363409 database_name : str ,
364410 vector_store_name : str ,
@@ -701,12 +747,15 @@ def register_tools(self):
701747 self .mcp .add_tool (self .get_table_schema )
702748 self .mcp .add_tool (self .execute_sql )
703749 self .mcp .add_tool (self .create_database )
750+ self .mcp .add_tool (self .explain_query )
751+ self .mcp .add_tool (self .explain_query_extended )
704752 if EMBEDDING_PROVIDER is not None :
705753 self .mcp .add_tool (self .create_vector_store )
706754 self .mcp .add_tool (self .list_vector_stores )
707755 self .mcp .add_tool (self .delete_vector_store )
708756 self .mcp .add_tool (self .insert_docs_vector_store )
709757 self .mcp .add_tool (self .search_vector_store )
758+
710759 logger .info ("Registered MCP tools explicitly." )
711760
712761 # --- Async Main Server Logic ---
0 commit comments