The psql Client
psql looks like a simple terminal prompt, but it is doing more work than it appears to.
Search across all documentation pages
psql looks like a simple terminal prompt, but it is doing more work than it appears to.
Underneath every \dt or \d+ is an ordinary SQL query against PostgreSQL's system catalogs, dressed up as a convenient shortcut.
psql is a thin SQL client that adds a layer of meta-commands, each of which is really a catalog query in disguise.\dt respects your search_path, why output formatting is a client concern, and why psql scripts behave differently from application code.search_path, non-interactive mode.psql is PostgreSQL's official command-line client, and at its core it does one thing: it opens a connection and sends whatever text you type to the server as a SQL statement.
The part that makes it feel like more than a raw SQL prompt is its set of meta-commands, the backslash-prefixed shortcuts like \dt, \d+, and \du.
Every one of those meta-commands is implemented by psql itself, not by the server, and each one works by generating and running a SQL query against PostgreSQL's system catalogs on your behalf.
\dt is really just a friendlier way of asking pg_catalog which tables exist in schemas on your current search_path.
That single fact explains a lot of psql's behavior at once: meta-commands respect the same search_path and permissions your login role has, because underneath they are ordinary, permission-checked queries.
A useful mental model is a translator standing next to you at a reference desk: you ask a short question in plain language, the translator turns it into the precise catalog lookup the librarian (the server) actually understands, and hands you back the answer formatted for reading.
Two states matter in every psql session, and keeping them separate avoids a lot of confusion: client-side state and server-side state.
Client-side state lives entirely inside the running psql process and includes things like your \pset output formatting, \timing, and any \set variables you have defined.
Server-side state lives on the connection itself and includes your current transaction status, your search_path, and any session-level GUC you have set with SET.
Reconnecting with \c starts a brand-new server-side session, which is why session GUCs set with plain SET do not survive a \c, while your client-side \pset settings do.
-- Client-side: formatting choice, lives only in this psql process
\x on
\timing on
-- Server-side: part of the actual database session
SET search_path = app, public;
BEGIN READ ONLY;That client/server split is also why scripting psql for CI or cron jobs needs a different mindset than typing interactively: a script has no human watching the prompt to notice a dangling transaction or a wrong database, so every assumption a human would catch by eye has to be made explicit instead.
Non-interactive mode, entered with -f for a script file or -c for a single command, disables most of the conveniences that make interactive use forgiving, such as automatic pager invocation and confirmation prompts.
Output-format meta-commands like \a (unaligned), \t (tuples only), and \pset footer off exist specifically to make psql usable as a data-pipe in shell scripts, turning what looks like an interactive tool into a legitimate piece of automation tooling.
That dual identity, human REPL and scriptable client, is also where most psql misuse in production starts, because habits that are harmless when a person is watching a terminal become dangerous once the same commands run unattended.
Connecting as a role with unrestricted write access "just to run a quick query" is the single most common way psql sessions cause incidents, since a typo in a WHERE clause has no human pause to catch it before it commits.
BEGIN READ ONLY and dedicated read-only roles exist precisely to make that mistake structurally harder, not just procedurally discouraged.
GUI tools like pgAdmin and DBeaver sit on top of the same underlying protocol psql uses, but they trade psql's scriptability for visual browsing, which makes the choice between them a workflow decision rather than a capability difference.
| Tool | Strength | Weakness | Best Fit |
|---|---|---|---|
psql (interactive) | Fast, ubiquitous, full meta-command catalog access | No visual browsing; steep memorization curve for new users | Ad-hoc investigation by people comfortable in a terminal |
psql (scripted, -f/-c) | Composable in shell pipelines and CI; fully reproducible | Needs explicit guardrails a human would otherwise provide | CI schema checks, cron jobs, migration runners |
| GUI client (pgAdmin, DBeaver) | Visual schema browsing; easier for less SQL-fluent teammates | Harder to version-control or automate | Mixed-skill teams, exploratory data browsing |
\dt and other backslash commands are special server features." They are entirely client-side conveniences; the server has no concept of a meta-command, only the catalog queries psql generates for you.\c keeps my session settings." \c opens a fresh server-side session, so anything set with SET (as opposed to ALTER ROLE ... SET) is lost, while client-side \pset settings persist.LIMIT alone makes an exploratory query safe." Without an ORDER BY, LIMIT returns an arbitrary slice of rows, and the query can still scan the whole table to produce it.It is PostgreSQL's official command-line SQL client, with an added layer of backslash meta-commands that translate to catalog queries.
It runs an ordinary, permission-checked SQL query against pg_catalog, scoped to your current search_path, and formats the result for the terminal.
\dt only shows tables visible on your current search_path, so a table in a schema not on that path will not appear unless you qualify the command, like \dt myschema.*.
Client-side state (like \pset formatting or \timing) lives only inside the running psql process; server-side state (like search_path or transaction status) lives on the actual database connection.
No - \c opens a new server-side session, so session-level SET values reset; only settings applied with ALTER ROLE ... SET persist across reconnects.
Non-interactive mode removes conveniences meant for a human at a terminal, such as the pager and some prompts, so a script must supply its own safety checks explicitly.
Only partly - LIMIT bounds the rows returned, but without ORDER BY it can still require scanning the whole table, so it protects your terminal output more than the server's work.
A read-only role makes accidental writes structurally impossible rather than merely discouraged, which matters most in the moment a typo slips into a WHERE clause.
They speak the same underlying protocol, but psql trades visual browsing for scriptability, making it the better fit for CI, automation, and terminal-first workflows.
\a (unaligned output), \t (tuples only), and \pset footer off strip the human-friendly formatting so the output can be piped into other tools cleanly.
It blocks any write statement from committing inside that transaction, converting a policy of caution into an enforced guarantee rather than something that depends on the person typing carefully.
No - a psql session, like any client, connects to a single database at a time, so switching databases with \c opens a new connection rather than adding a second one.
Stack versions: This page is conceptual and describes
psqlclient behavior consistent across current PostgreSQL major versions, including PostgreSQL 18.4 (stable 18, maintenance line 17).
Reviewed by Chris St. John·Last updated Jul 15, 2026