Psql
Drive `psql` against the LOCAL dev cluster built from source in this workspace (Unix socket `/tmp`, trust auth, db `postgres`, ports 5432 debug / 5433 asan). Covers the connection idiom, high-yield meta-commands (`\d+`, `\df+`, `\sf`, `\timing`, `\watch`, `\gexec`, `\errverbose`), session-only debug GUCs (`client_min_messages`, `log_min_messages`, `EXPLAIN (ANALYZE, BUFFERS, WAL)`, `enable_seqscan`/`enable_hashjoin`/etc.), runtime introspection of *this* postmaster (`pg_stat_activity`, `pg_backend_memory_contexts`, `pg_locks`, `pg_log_backend_memory_contexts`), the held-PID handoff to lldb (`PGAPPNAME=hold` + `pg_sleep`), and what's safe vs not on a disposable dev cluster. **Use this skill proactively whenever the user wants to run SQL interactively or by script against the locally-built Postgres, reproduce a backend bug on the dev cluster, watch a backend's memory contexts grow, capture a backend PID for the debugger, force a specific plan shape via `SET enable_*`, run `EXPLAIN ANALYZE` against the dev DB, time a query with `\timing`, get the connection string for the dev cluster, or do any psql-driven exploration of an internals-flavored question — even when the user doesn't say the word "psql".** Skip for: questions about production / managed PostgreSQL instances at someone else's company; SQL written for app frameworks (Django/Rails/SQLAlchemy/Prisma/JDBC ORM/`models.py`/migration files); pure "how does pg_class / OID work" catalog-structure questions (use `catalog-conventions`); subsystem-internals questions answered from source code (cost_hashjoin, XLogInsert, deadlock detector, LWLock — use `executor-and-planner` / `wal-and-xlog` / `locking`); first-time build-from-source or start/stop lifecycle (use `build-and-run`); lldb/gdb stepping itself, single-user mode, sanitizer setup (use `debugging`); and one-shot read-only agent queries that should go through the `postgres-dev` MCP at `.mcp.json` instead of psql.From its SKILL.md
npx -y skills add matejformanek/postgres-claude --skill psqlAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
13.1 KB, ~2.9k tokens by cl100k_base, as published. Nobody here has run it
Working with the local dev cluster via psql
Default connection: psql -h /tmp -d postgres — Unix socket at /tmp, trust
auth (no password), database postgres, superuser = your macOS username.
Server log is dev/data-debug/server.log (tail it with /pg-tail-log).
When to reach for what
- psql — anything interactive, anything writing, anything that needs meta
commands (
\d,\timing,\watch,\errverbose), anything debug-flavored (changingclient_min_messages, capturing a backend PID, running EXPLAIN ANALYZE in a loop). Default tool. - postgres-dev MCP — read-only one-shots from a planning/exploration loop (sampling rows, schema introspection inside agent reasoning). Strictly SELECT, no meta-commands, no session knobs. Treat as a convenience for agents, not a substitute for psql.
Daily-loop one-liners (assumes /pg-start already ran)
export PATH="$PWD/dev/install-debug/bin:$PATH"
export PGDATA="$PWD/dev/data-debug"
psql -h /tmp -d postgres # interactive
psql -h /tmp -d postgres -c 'SELECT version();' # one-shot
psql -h /tmp -d postgres -f /tmp/repro.sql # script
psql -h /tmp -d postgres -X -P pager=off -At -c '<sql>' # script-friendly
-X skips ~/.psqlrc, -A unaligned, -t tuples-only, -P pager=off keeps
pipes clean.
High-yield meta-commands for backend work
| Command | What it does |
|---|---|
\conninfo | Confirm socket, port, db, user, PID. |
\d <name> / \d+ <name> | Schema for table / index / view. + adds storage params and tablespace. |
\df+ <fn> / \sf <fn> | Function signature / full source — works for SQL & PL/pgSQL bodies. |
\sv <view> | Source of a view. |
\dn+, \dt+, \di+, \dm+ | Schemas / tables / indexes / matviews with sizes. |
\d+ pg_catalog.<rel> | Walk a catalog when debugging planner / cache code. |
\dconfig <pattern> | All GUCs matching pattern, current values + source. |
\timing on | Per-query wall time. |
\watch <sec> | Re-run last query every N seconds — great for pg_stat_activity / pg_locks while reproducing. |
\errverbose | After an error: print code, detail, hint, file:line of the ereport(). The file:line is the elog location in the backend source — pair with the corpus. |
\gexec | Run a query whose result is itself SQL; e.g., generate DROP TABLE … from pg_tables. |
\set ON_ERROR_STOP on | Make scripted runs abort on first error (default off is footgunny). |
\copy table FROM '/path' WITH (FORMAT csv) | Client-side copy — bypasses server-side permissions. |
Session knobs that surface backend behavior
-- Show every DEBUG2-and-louder log line in psql (no need to tail the log).
SET client_min_messages = DEBUG2;
-- Have the server log them too (so they land in dev/data-debug/server.log).
SET log_min_messages = DEBUG2;
-- Log every statement + duration + parse/plan/exec break-down.
SET log_statement = 'all';
SET log_duration = on;
SET log_min_duration_statement = 0;
-- Make EXPLAIN ANALYZE useful for buffer / WAL / IO debugging.
EXPLAIN (ANALYZE, BUFFERS, WAL, SETTINGS, VERBOSE) <query>;
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) <query>; -- machine-parseable
-- Force a specific plan shape to confirm a planner hypothesis.
-- enable_* don't HARD-disable a node type — they apply a large cost
-- penalty (disable_cost), so the planner picks the next-best plan.
-- If every alternative is also disabled, the "disabled" node still wins.
SET enable_seqscan = off; -- and friends: enable_hashjoin / _nestloop / _indexonlyscan
SET work_mem = '4MB'; -- sort/hash spill threshold
SET jit = off; -- rule JIT in/out when timing things
All SET is session-scoped — exiting psql resets. Use SET LOCAL inside a
transaction to scope to that txn.
Runtime introspection — what is the backend doing?
-- All sessions + their state, query, wait event, backend PID.
SELECT pid, state, wait_event_type, wait_event, query
FROM pg_stat_activity
WHERE backend_type = 'client backend';
-- Memory contexts of THIS backend — top consumers first.
-- Columns (PG 17+): name, ident, type, level, path int4[], total_bytes,
-- total_nblocks, free_bytes, free_chunks, used_bytes.
-- `path` is the array of ancestor context_ids from TopMemoryContext down.
SELECT name, type, level, path, total_bytes/1024 AS kb, used_bytes/1024 AS used_kb
FROM pg_backend_memory_contexts
ORDER BY total_bytes DESC LIMIT 20;
-- Memory contexts of ANOTHER backend (PG 14+).
SELECT pg_log_backend_memory_contexts(<pid>);
-- → lands in dev/data-debug/server.log; tail it.
-- Live locks + blockers.
SELECT locktype, relation::regclass, mode, granted, pid
FROM pg_locks ORDER BY relation, pid;
-- Buffer cache hit ratio per relation (run after a workload).
SELECT relname,
heap_blks_read AS read, heap_blks_hit AS hit,
round(heap_blks_hit::numeric / nullif(heap_blks_hit + heap_blks_read,0), 3) AS ratio
FROM pg_statio_user_tables ORDER BY hit + read DESC LIMIT 10;
Capturing a backend PID for gdb/lldb
Every psql connection causes the postmaster to fork() a fresh backend;
the PID you get from pg_backend_pid() is THAT backend's pid (NOT
psql's client-side pid). See knowledge/architecture/process-model.md.
Quick PID grab from inside the session:
SELECT pg_backend_pid();
Then in another shell: /pg-attach <pid> (the slash command wraps lldb with
breakpoints on errstart and MemoryContextStats pre-set).
Race-safe held-PID handoff — when you need to attach BEFORE a query
runs (so lldb sees its execution), use the PGAPPNAME=hold + pg_sleep
pattern. PGAPPNAME is a libpq env var (NOT a psql \set variable —
that won't propagate):
# 1. Tag a holding backend with application_name='hold' and pin it open.
PGAPPNAME=hold psql -h /tmp -d postgres -X -c 'SELECT pg_sleep(600);' &
# 2. From a second psql, find the PID.
PID=$(psql -h /tmp -d postgres -At -c \
"SELECT pid FROM pg_stat_activity WHERE application_name='hold'")
# 3. Attach.
/pg-attach "$PID"
# 4. From a THIRD psql session (same application_name='hold' if you
# want to drive queries through the attached backend), run your
# actual repro.
If the backend you want to debug doesn't exist yet (e.g., you're studying
startup), use single-user mode instead — see .claude/skills/debugging/SKILL.md.
Memory-leak workflow on the debug build
The build defaults (-Ddebug=true -Dcassert=true) wire in the asserts and
the clobber-freed-memory machinery. To hunt a suspected leak:
- Note baseline (MessageContext is the per-message context, reset
between client protocol messages — growth across iterations is the
leak signature for a per-message leak. Other commonly-watched
contexts:
CacheMemoryContext,ExecutorState,PortalContext):SELECT name, total_bytes FROM pg_backend_memory_contexts WHERE name='MessageContext'; - Run the suspect workload N times (
\watchis your friend). - Re-check the context — growth across iterations is the leak signature.
- To pin the leak to a callsite, attach lldb (
/pg-attach <pid>) and set a breakpoint onMemoryContextAllocfiltered to the suspect context. - macOS-specific:
MallocStackLogging=1env on the postmaster beforepg_ctl startmakesleaks <pid>produce real backtraces.
Safe vs not-safe on the dev cluster
The dev cluster is disposable — dev/data-debug/ can be wiped at any
time via /pg-fresh. So:
- ✅
DROP DATABASE,DROP SCHEMA CASCADE,TRUNCATE, anything destructive on the dev cluster. - ✅
ALTER SYSTEM SET …for testing GUCs (writespostgresql.auto.conf; reset withALTER SYSTEM RESET <guc>thenSELECT pg_reload_conf()). - ✅
CREATE EXTENSIONwhatever's compiled intodev/install-debug/share/extension/. - ⚠️ Avoid editing
pg_catalog.*directly — even on the dev cluster it often crashes the backend in interesting-but-not-useful ways. If you need to perturb catalogs, write a regression test instead. - ⚠️
DELETE FROM pg_class …is exactly the wrong way to do anything.
Connection-string variants for psql / libpq tools
Built from the trust + socket defaults:
postgresql:///postgres?host=/tmp # the canonical one
postgresql://$USER@/postgres?host=/tmp # explicit user
postgresql:///postgres?host=/tmp&application_name=repro # tag the session for pg_stat_activity
postgresql:///postgres?host=/tmp&options=-c%20client_min_messages%3DDEBUG2 # set GUC in URL
The MCP at .mcp.json uses the canonical form. If you need a different
database, edit .mcp.json rather than passing flags ad-hoc.
Common gotchas
psql -h db.acme.com …(or any hostname / managed-PG vendor) is the WRONG tool here. This skill is for the LOCAL dev cluster built from source — Unix socket/tmp, trust auth, dbpostgres. If the prompt names a hostname, a managed-PG vendor (RDS / Cloud SQL / Supabase / Neon / Aurora), or talks about touching prod data, stop. Use the production-PG tooling for that team, NOT this skill.psql: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory— the server isn't running, orunix_socket_directoriesisn't/tmp. Checkdev/data-debug/postgresql.conf;/pg-startsets this.role "postgres" does not exist—initdbmakes the superuser match$USER, not literallypostgres. Connect as your shell user; the database calledpostgresdoes exist.- Hung session blocks something —
\watchpg_stat_activityto find the blocker PID, thenSELECT pg_terminate_backend(<pid>). SET client_min_messages = DEBUG2floods psql — fine for one query, painful for an interactive session. UseSET LOCALinsideBEGIN/COMMITfor scoped noise.
Cross-references
.claude/skills/build-and-run/SKILL.md— cluster lifecycle (/pg-start,/pg-stop,/pg-restart,/pg-fresh) and PATH/PGDATA wiring this skill assumes..claude/skills/debugging/SKILL.md— held-PID handoff to lldb (PGAPPNAME=hold+pg_sleep); single-user mode when the backend you want doesn't exist yet..claude/skills/error-handling/SKILL.md— what\errverboseis showing (theereport()machinery)..claude/skills/memory-contexts/SKILL.md— interpretingpg_backend_memory_contexts/pg_log_backend_memory_contextsoutput.knowledge/architecture/process-model.md— how a psql connection becomes a postmasterfork()+ per-connection backend.knowledge/data-structures/snapshot-lifecycle.md— what\d+ <table>is computing under the hood when it touches catalog snapshots.knowledge/subsystems/storage-buffer.md— whatBUFFERSinEXPLAIN (ANALYZE, BUFFERS)is counting..mcp.json— the read-onlypostgres-devMCP endpoint for one-shot agent queries (alternative to psql for non-interactive lookups).
What ships with it: 1 file
3.7 KB alongside SKILL.md
evals/
- trigger-eval.json3.7 KB