Database explorer module for PhoenixKit.
Provides metadata, stats, and paginated previews for Postgres tables
so the admin UI can browse data without exposing full SQL access.
Live updates ride on Postgres LISTEN/NOTIFY via the
PhoenixKitDb.Listener GenServer.
Live Updates
When a table is being viewed, changes to that table trigger automatic refreshes. This requires:
- The
ListenerGenServer running (started via the host'sPhoenixKit.Supervisorfrom this module'schildren/0callback). - A notification trigger on the table being viewed — installed
lazily by
ensure_trigger/2on first view.
Summary
Types
An identifier accepted by the schema/table-name validator.
Functions
Aggregated Postgres stats for all user tables.
Whether the DB module is enabled.
Ensures the notification function exists and creates a trigger on the table.
Fetches a single row by ID from a table.
Whether a table has a notification trigger installed.
Lists tables + stats with pagination and search.
Lists all tables that have notification triggers installed.
Removes all notification triggers from all tables.
Removes the notification trigger from a table.
Returns table metadata and a row preview window.
Types
@type identifier_string() :: String.t()
An identifier accepted by the schema/table-name validator.
Functions
@spec database_stats() :: %{ table_count: non_neg_integer(), approx_rows: non_neg_integer(), total_size_bytes: non_neg_integer(), database_size_bytes: non_neg_integer() }
Aggregated Postgres stats for all user tables.
@spec enabled?() :: boolean()
Whether the DB module is enabled.
Reads from the DB-backed settings table. Defensive against three failure modes that can hit before/around DB availability:
rescue _: DB not running, table missing, schema mismatch, etc.catch :exit, _: connection pool checkoutEXIT(e.g. when a test sandbox owner has just stopped — test-environment artifact, but harmless to handle in production code too).
All branches return false so callers don't need to special-case
startup ordering.
@spec ensure_trigger(identifier_string(), identifier_string()) :: :ok | {:error, :invalid_identifier | term()}
Ensures the notification function exists and creates a trigger on the table.
Idempotent. A concurrent caller racing to create the same trigger
surfaces as :ok rather than an error — the win condition is "the
trigger exists", and Postgres' duplicate_object reply on the loser
is folded back into success.
Returns :ok on success, {:error, :invalid_identifier} if the
schema/table contain unsafe characters, or {:error, reason} on a
Postgres error.
@spec fetch_row(identifier_string() | nil, identifier_string(), term()) :: {:ok, map()} | {:error, :not_found | :invalid_id | :invalid_identifier | term()}
Fetches a single row by ID from a table.
Returns {:ok, row_map} or {:error, :not_found | :invalid_id | :invalid_identifier | term()}. :invalid_identifier covers the case
where schema or table contains characters that can't be safely
quoted into SQL — this surfaces graceful "table not found" UX
instead of a 500.
@spec has_trigger?(identifier_string(), identifier_string()) :: boolean()
Whether a table has a notification trigger installed.
Lists tables + stats with pagination and search.
Lists all tables that have notification triggers installed.
@spec remove_all_triggers() :: :ok
Removes all notification triggers from all tables.
@spec remove_trigger(identifier_string(), identifier_string()) :: :ok | {:error, :invalid_identifier | term()}
Removes the notification trigger from a table.
@spec table_preview(identifier_string() | nil, identifier_string(), map()) :: map()
Returns table metadata and a row preview window.
When schema or table is malformed, returns the empty preview
shape rather than crashing — the LV uses this signal to render a
graceful "table not found" message.