Coverage for src / sql_tool / cli / commands / service.py: 62%
34 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-14 15:28 -0500
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-14 15:28 -0500
1"""Service and maintenance CLI commands.
3Thin CLI layer: typer decorators, argument parsing, output formatting.
4Business logic delegated to core.postgres module.
5"""
7from __future__ import annotations
9from typing import Annotated
11import typer
13from sql_tool.cli.commands._shared import get_client, output_result
14from sql_tool.core.postgres import (
15 check_server,
16 kill_backend,
17 list_user_tables,
18 vacuum_tables,
19)
21service_app = typer.Typer(help="Service and maintenance commands")
24@service_app.callback(invoke_without_command=True)
25def service_callback(
26 ctx: typer.Context,
27) -> None:
28 if not ctx.invoked_subcommand:
29 typer.echo(ctx.get_help())
30 raise typer.Exit()
33@service_app.command("vacuum")
34def vacuum_command(
35 ctx: typer.Context,
36 table_arg: Annotated[
37 str | None,
38 typer.Argument(help="Table name to vacuum"),
39 ] = None,
40 full: Annotated[
41 bool,
42 typer.Option("--full", help="Run VACUUM FULL (locks table, rewrites)"),
43 ] = False,
44 all_tables: Annotated[
45 bool,
46 typer.Option("--all", help="Vacuum all user tables"),
47 ] = False,
48) -> None:
49 """VACUUM marks dead rows as reusable. VACUUM FULL rewrites tables but requires exclusive lock."""
50 if not table_arg and not all_tables:
51 typer.echo("Error: Must specify table name or --all", err=True)
52 raise typer.Exit(2)
54 with get_client(ctx) as client:
55 table_names = list_user_tables(client) if all_tables else [table_arg] # type: ignore[list-item]
57 count = vacuum_tables(client, table_names, full=full)
59 typer.echo(f"Vacuumed {count} table(s)")
62@service_app.command("kill")
63def kill_command(
64 ctx: typer.Context,
65 pid: Annotated[int, typer.Argument(help="Backend process ID to terminate")],
66 cancel: Annotated[
67 bool,
68 typer.Option("--cancel", help="Cancel query only (don't kill connection)"),
69 ] = False,
70) -> None:
71 """pg_terminate_backend() kills the connection. pg_cancel_backend() cancels query but keeps connection alive."""
72 with get_client(ctx) as client:
73 success = kill_backend(client, pid, cancel=cancel)
75 action = "Cancelled" if cancel else "Terminated"
76 if success:
77 typer.echo(f"{action} backend {pid}")
78 else:
79 typer.echo(f"Error: Backend {pid} not found or already terminated", err=True)
80 raise typer.Exit(1)
83@service_app.command("check")
84def check_command(ctx: typer.Context) -> None:
85 """
86 Check PostgreSQL server connectivity and version.
88 Reports server version, current database, connected user, and uptime.
89 Useful for verifying connection parameters and server availability.
90 """
91 with get_client(ctx) as client:
92 result = check_server(client)
94 output_result(ctx, result)