1use serde_json::{json, Value};
2use tokio_postgres::Client;
3use crate::errors::Result as MCPResult;
4
5pub async fn show_active_transactions(client: &Client, _params: &Option<&Value>) -> MCPResult<Value> {
7 let rows = client
8 .query(
9 "SELECT pid, usename, application_name, state, xact_start, query_start, query
10 FROM pg_stat_activity
11 WHERE xact_start IS NOT NULL AND pid != pg_backend_pid()
12 ORDER BY xact_start ASC",
13 &[],
14 )
15 .await?;
16
17 let transactions: Vec<Value> = rows
18 .iter()
19 .map(|row| {
20 json!({
21 "pid": row.get::<_, i32>(0),
22 "user": row.get::<_, String>(1),
23 "application": row.get::<_, Option<String>>(2),
24 "state": row.get::<_, String>(3),
25 "xact_start": row.get::<_, String>(4),
26 "query_start": row.get::<_, String>(5),
27 "query": row.get::<_, Option<String>>(6),
28 })
29 })
30 .collect();
31
32 Ok(json!({ "transactions": transactions }))
33}
34
35pub async fn show_locks(client: &Client, _params: &Option<&Value>) -> MCPResult<Value> {
37 let rows = client
38 .query(
39 "SELECT l.pid, a.usename, a.application_name, l.mode, l.granted, l.fastpath,
40 a.query_start, a.query
41 FROM pg_locks l
42 JOIN pg_stat_activity a ON l.pid = a.pid
43 WHERE l.pid != pg_backend_pid()
44 ORDER BY l.pid, l.mode",
45 &[],
46 )
47 .await?;
48
49 let locks: Vec<Value> = rows
50 .iter()
51 .map(|row| {
52 json!({
53 "pid": row.get::<_, i32>(0),
54 "user": row.get::<_, String>(1),
55 "application": row.get::<_, Option<String>>(2),
56 "lock_type": row.get::<_, String>(3),
57 "granted": row.get::<_, bool>(4),
58 "fastpath": row.get::<_, bool>(5),
59 "query_start": row.get::<_, Option<String>>(6),
60 "query": row.get::<_, Option<String>>(7),
61 })
62 })
63 .collect();
64
65 Ok(json!({ "locks": locks }))
66}
67
68pub async fn show_waiting_locks(client: &Client, _params: &Option<&Value>) -> MCPResult<Value> {
70 let rows = client
71 .query(
72 "SELECT l.pid, a.usename, l.mode, a.query_start, a.query
73 FROM pg_locks l
74 JOIN pg_stat_activity a ON l.pid = a.pid
75 WHERE NOT l.granted AND l.pid != pg_backend_pid()
76 ORDER BY a.query_start ASC",
77 &[],
78 )
79 .await?;
80
81 let waiting: Vec<Value> = rows
82 .iter()
83 .map(|row| {
84 json!({
85 "pid": row.get::<_, i32>(0),
86 "user": row.get::<_, String>(1),
87 "lock_type": row.get::<_, String>(2),
88 "query_start": row.get::<_, String>(3),
89 "query": row.get::<_, Option<String>>(4),
90 })
91 })
92 .collect();
93
94 Ok(json!({ "waiting_locks": waiting }))
95}
96
97pub async fn show_transaction_isolation(client: &Client, _params: &Option<&Value>) -> MCPResult<Value> {
100 let rows = client
101 .query("SHOW transaction_isolation", &[])
102 .await?;
103
104 let level = rows[0].get::<_, String>(0);
105
106 Ok(json!({
107 "isolation_level": level,
108 "available_levels": ["serializable", "repeatable read", "read committed", "read uncommitted"]
109 }))
110}
111
112pub async fn show_deadlocks(client: &Client, _params: &Option<&Value>) -> MCPResult<Value> {
120 let rows = client
122 .query(
123 "SELECT a.pid, a.usename::text, a.application_name, a.state,
124 a.query_start::text, a.query,
125 pg_blocking_pids(a.pid) AS blocked_by,
126 (SELECT count(*) FROM pg_stat_activity
127 WHERE pid = ANY(pg_blocking_pids(a.pid))) AS blockers_count
128 FROM pg_stat_activity a
129 WHERE cardinality(pg_blocking_pids(a.pid)) > 0
130 AND a.pid != pg_backend_pid()
131 ORDER BY a.query_start ASC",
132 &[],
133 )
134 .await?;
135
136 let blocked: Vec<Value> = rows
137 .iter()
138 .map(|row| {
139 json!({
140 "pid": row.get::<_, i32>(0),
141 "user": row.get::<_, String>(1),
142 "application": row.get::<_, Option<String>>(2),
143 "state": row.get::<_, String>(3),
144 "query_start": row.get::<_, String>(4),
145 "query": row.get::<_, Option<String>>(5),
146 "blocked_by": row.get::<_, Vec<i32>>(6),
147 "blocker_count": row.get::<_, i64>(7),
148 "advisory": "Deadlocks are auto-detected and resolved within deadlock_timeout (default 1s). These are currently blocked processes — potential deadlock situations."
149 })
150 })
151 .collect();
152
153 Ok(json!({ "blocked_processes": blocked, "count": blocked.len() }))
154}
155
156pub async fn show_autocommit_status(_client: &Client, _params: &Option<&Value>) -> MCPResult<Value> {
164 Ok(json!({
165 "autocommit": true,
166 "status": "always_on",
167 "detail": "PostgreSQL always operates in autocommit mode at the wire protocol level. Client-side autocommit control is implemented by your database driver, not the server.",
168 "note": "The autocommit GUC was removed in PostgreSQL 7.4 (2003) and is not available in any supported version."
169 }))
170}
171
172pub async fn show_transaction_timeout(client: &Client, _params: &Option<&Value>) -> MCPResult<Value> {
174 let rows = client
175 .query("SHOW statement_timeout", &[])
176 .await?;
177
178 let timeout = rows[0].get::<_, String>(0);
179
180 Ok(json!({
181 "statement_timeout": timeout
182 }))
183}