feat: add column sort, SQL formatter, table stats, insert dialog, saved queries & sessions monitor
- Column sort by header click in table view (ASC/DESC/none cycle, server-side) - SQL formatter with Format button and Shift+Alt+F keybinding (sql-formatter) - Table size and row count display in schema tree via pg_class - Insert row dialog with column type hints and auto-skip for identity columns - Saved queries (bookmarks) with CRUD backend, sidebar panel, and save dialog - Active sessions monitor (pg_stat_activity) with auto-refresh, cancel & terminate Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -507,3 +507,83 @@ pub async fn manage_role_membership(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_sessions(
|
||||
state: State<'_, AppState>,
|
||||
connection_id: String,
|
||||
) -> TuskResult<Vec<SessionInfo>> {
|
||||
let pools = state.pools.read().await;
|
||||
let pool = pools
|
||||
.get(&connection_id)
|
||||
.ok_or(TuskError::NotConnected(connection_id))?;
|
||||
|
||||
let rows = sqlx::query(
|
||||
"SELECT pid, usename, datname, state, query, \
|
||||
query_start::text, wait_event_type, wait_event, \
|
||||
client_addr::text \
|
||||
FROM pg_stat_activity \
|
||||
WHERE datname IS NOT NULL \
|
||||
ORDER BY query_start DESC NULLS LAST",
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
.map_err(TuskError::Database)?;
|
||||
|
||||
let sessions = rows
|
||||
.iter()
|
||||
.map(|row| SessionInfo {
|
||||
pid: row.get("pid"),
|
||||
usename: row.get("usename"),
|
||||
datname: row.get("datname"),
|
||||
state: row.get("state"),
|
||||
query: row.get("query"),
|
||||
query_start: row.get("query_start"),
|
||||
wait_event_type: row.get("wait_event_type"),
|
||||
wait_event: row.get("wait_event"),
|
||||
client_addr: row.get("client_addr"),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(sessions)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn cancel_query(
|
||||
state: State<'_, AppState>,
|
||||
connection_id: String,
|
||||
pid: i32,
|
||||
) -> TuskResult<bool> {
|
||||
let pools = state.pools.read().await;
|
||||
let pool = pools
|
||||
.get(&connection_id)
|
||||
.ok_or(TuskError::NotConnected(connection_id))?;
|
||||
|
||||
let row = sqlx::query("SELECT pg_cancel_backend($1)")
|
||||
.bind(pid)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.map_err(TuskError::Database)?;
|
||||
|
||||
Ok(row.get::<bool, _>(0))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn terminate_backend(
|
||||
state: State<'_, AppState>,
|
||||
connection_id: String,
|
||||
pid: i32,
|
||||
) -> TuskResult<bool> {
|
||||
let pools = state.pools.read().await;
|
||||
let pool = pools
|
||||
.get(&connection_id)
|
||||
.ok_or(TuskError::NotConnected(connection_id))?;
|
||||
|
||||
let row = sqlx::query("SELECT pg_terminate_backend($1)")
|
||||
.bind(pid)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.map_err(TuskError::Database)?;
|
||||
|
||||
Ok(row.get::<bool, _>(0))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user