2424# Replace localhost with 127.0.0.1 to avoid DNS issues
2525if 'localhost' in DATABASE_URL :
2626 DATABASE_URL = DATABASE_URL .replace ('localhost' , '127.0.0.1' )
27- logger .info (f "Replaced localhost with 127.0.0.1 in DATABASE_URL" )
27+ logger .info ("Replaced localhost with 127.0.0.1 in DATABASE_URL" )
2828
2929logger .info (f"Using DATABASE_URL: { DATABASE_URL .replace (DATABASE_URL .split ('@' )[0 ].split ('//' )[1 ], '***:***' )} " )
3030
@@ -47,7 +47,7 @@ async def get_pool():
4747 logger .info ("✅ Database connection pool created successfully" )
4848 except Exception as e :
4949 logger .error (f"❌ Failed to create database connection pool: { str (e )} " )
50- logger .error (f "Connection URL format: postgresql://user:***@host:port/database" )
50+ logger .error ("Connection URL format: postgresql://user:***@host:port/database" )
5151 raise Exception (f"Database connection failed: { str (e )} " )
5252 return connection_pool
5353
@@ -896,8 +896,7 @@ async def PostgreSQL_get_sequence_value(sequence_name: str, schema_name: str = "
896896 "current_value" : result [0 ]["current_value" ],
897897 "next_value" : result [0 ]["next_value" ]
898898 }
899- except Exception as e :
900- # If sequence hasn't been used yet, currval will fail, so just get nextval
899+ except asyncpg .exceptions .ObjectNotInPrerequisiteStateError :
901900 query = f"SELECT nextval('{ schema_name } .{ sequence_name } ') as next_value"
902901 result = await execute_query (query )
903902 return {
@@ -4813,28 +4812,6 @@ async def PostgreSQL_get_connection_pool_stats() -> Dict[str, Any]:
48134812 rows = await execute_query (query )
48144813 return rows [0 ] if rows else {}
48154814
4816- @mcp .tool ()
4817- async def PostgreSQL_get_checkpoint_stats () -> Dict [str , Any ]:
4818- """Get checkpoint activity and WAL statistics for performance monitoring."""
4819- query = """
4820- SELECT
4821- checkpoints_timed,
4822- checkpoints_req as checkpoints_requested,
4823- checkpoint_write_time,
4824- checkpoint_sync_time,
4825- buffers_checkpoint,
4826- buffers_clean,
4827- maxwritten_clean,
4828- buffers_backend,
4829- buffers_backend_fsync,
4830- buffers_alloc,
4831- stats_reset
4832- FROM pg_stat_bgwriter
4833- """
4834-
4835- rows = await execute_query (query )
4836- return rows [0 ] if rows else {}
4837-
48384815@mcp .tool ()
48394816async def PostgreSQL_get_cache_hit_ratios () -> Dict [str , Any ]:
48404817 """Calculate cache hit ratios for buffer and index performance analysis."""
@@ -5002,34 +4979,6 @@ async def PostgreSQL_get_table_size_summary() -> List[Dict[str, Any]]:
50024979 rows = await execute_query (query )
50034980 return rows
50044981
5005- @mcp .tool ()
5006- async def PostgreSQL_get_index_usage_stats () -> List [Dict [str , Any ]]:
5007- """Get detailed index usage statistics and effectiveness metrics."""
5008- query = """
5009- SELECT
5010- t.schemaname,
5011- t.tablename,
5012- i.indexrelname as index_name,
5013- i.idx_tup_read as index_reads,
5014- i.idx_tup_fetch as index_fetches,
5015- pg_size_pretty(pg_relation_size(i.indexrelid)) as index_size,
5016- pg_relation_size(i.indexrelid) as index_bytes,
5017- CASE
5018- WHEN i.idx_tup_read = 0 THEN 'UNUSED'
5019- WHEN i.idx_tup_read < 100 THEN 'LOW USAGE'
5020- WHEN i.idx_tup_read < 1000 THEN 'MODERATE USAGE'
5021- ELSE 'HIGH USAGE'
5022- END as usage_category,
5023- ROUND(100.0 * i.idx_tup_fetch / GREATEST(i.idx_tup_read, 1), 2) as selectivity_ratio
5024- FROM pg_stat_user_indexes i
5025- JOIN pg_stat_user_tables t ON i.relid = t.relid
5026- WHERE i.schemaname NOT IN ('information_schema', 'pg_catalog')
5027- ORDER BY index_bytes DESC, i.idx_tup_read DESC
5028- """
5029-
5030- rows = await execute_query (query )
5031- return rows
5032-
50334982@mcp .tool ()
50344983async def PostgreSQL_get_replication_stats () -> List [Dict [str , Any ]]:
50354984 """Get replication slot and standby server statistics."""
@@ -5224,28 +5173,6 @@ async def PostgreSQL_get_table_io_stats() -> List[Dict[str, Any]]:
52245173 rows = await execute_query (query )
52255174 return rows
52265175
5227- @mcp .tool ()
5228- async def PostgreSQL_get_tablespace_usage () -> List [Dict [str , Any ]]:
5229- """Get tablespace usage information and disk space statistics."""
5230- query = """
5231- SELECT
5232- ts.spcname as tablespace_name,
5233- pg_catalog.pg_tablespace_location(ts.oid) as location,
5234- pg_size_pretty(pg_tablespace_size(ts.oid)) as size,
5235- pg_tablespace_size(ts.oid) as size_bytes,
5236- COALESCE(owner.rolname, 'Unknown') as owner,
5237- array_to_string(ts.spcacl, ', ') as permissions,
5238- COUNT(c.oid) as objects_count
5239- FROM pg_tablespace ts
5240- LEFT JOIN pg_authid owner ON ts.spcowner = owner.oid
5241- LEFT JOIN pg_class c ON c.reltablespace = ts.oid
5242- GROUP BY ts.oid, ts.spcname, owner.rolname, ts.spcacl
5243- ORDER BY pg_tablespace_size(ts.oid) DESC
5244- """
5245-
5246- rows = await execute_query (query )
5247- return rows
5248-
52495176@mcp .tool ()
52505177async def PostgreSQL_get_role_attributes () -> List [Dict [str , Any ]]:
52515178 """Get detailed information about database roles and their attributes."""
@@ -6257,44 +6184,6 @@ async def PostgreSQL_get_trigger_performance_impact() -> list[dict]:
62576184 rows = await execute_query (query )
62586185 return rows
62596186
6260- @mcp .tool ()
6261- async def PostgreSQL_get_vacuum_analyze_recommendations () -> list [dict ]:
6262- """Generate vacuum and analyze recommendations based on table activity."""
6263- query = """
6264- SELECT
6265- schemaname,
6266- tablename,
6267- pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as table_size,
6268- n_dead_tup as dead_tuples,
6269- n_live_tup as live_tuples,
6270- ROUND((n_dead_tup::float / NULLIF(n_live_tup + n_dead_tup, 0)) * 100, 2) as dead_percentage,
6271- last_vacuum,
6272- last_autovacuum,
6273- EXTRACT(DAYS FROM (now() - COALESCE(last_vacuum, last_autovacuum))) as days_since_vacuum,
6274- last_analyze,
6275- last_autoanalyze,
6276- EXTRACT(DAYS FROM (now() - COALESCE(last_analyze, last_autoanalyze))) as days_since_analyze,
6277- n_tup_ins + n_tup_upd + n_tup_del as total_modifications,
6278- CASE
6279- WHEN n_dead_tup > 10000 AND n_dead_tup > n_live_tup * 0.2 THEN 'URGENT: Manual VACUUM needed'
6280- WHEN n_dead_tup > 5000 AND n_dead_tup > n_live_tup * 0.1 THEN 'Recommend VACUUM'
6281- WHEN EXTRACT(DAYS FROM (now() - COALESCE(last_vacuum, last_autovacuum))) > 7 THEN 'Consider scheduled VACUUM'
6282- ELSE 'VACUUM OK'
6283- END as vacuum_recommendation,
6284- CASE
6285- WHEN EXTRACT(DAYS FROM (now() - COALESCE(last_analyze, last_autoanalyze))) > 7 AND (n_tup_ins + n_tup_upd + n_tup_del) > 1000 THEN 'ANALYZE recommended'
6286- WHEN EXTRACT(DAYS FROM (now() - COALESCE(last_analyze, last_autoanalyze))) > 14 THEN 'ANALYZE overdue'
6287- ELSE 'ANALYZE OK'
6288- END as analyze_recommendation
6289- FROM pg_stat_user_tables
6290- WHERE schemaname NOT IN ('information_schema', 'pg_catalog')
6291- ORDER BY dead_percentage DESC, total_modifications DESC
6292- LIMIT 30
6293- """
6294-
6295- rows = await execute_query (query )
6296- return rows
6297-
62986187@mcp .tool ()
62996188async def PostgreSQL_get_connection_pool_analysis () -> list [dict ]:
63006189 """Analyze connection pool efficiency and usage patterns."""
0 commit comments