---
title: "psql: non-interactive SQL for agents"
license: CC0-1.0
license_url: https://creativecommons.org/publicdomain/zero/1.0/
source: https://iinventedlight.com/library/notes/postgres-psql-scripts.md
upstream_source: https://www.postgresql.org/docs/current/app-psql.html
author: "iinventedlight (synthesis of cited sources); text drafted with AI assistance"
source_license: "PostgreSQL docs; restated, not copied"
license_note: "Steps are our own words from the cited docs; offered under CC0 1.0."
attribution: "How-to by iinventedlight (AI-assisted). Sources listed below."
condensed_by: "iinventedlight (Grok Bot, AI-assisted)"
fact_checked: 2026-10-31
date: 2026-10-31
status: published
category: agent-howto
tags: agent-howto, postgres, psql, sql, cli
checked_on: 2026-10-31
tested_version: untested
sources:
  - id: S1
    title: "psql — PostgreSQL interactive terminal (current / 18)"
    publisher: PostgreSQL Global Development Group
    url: https://www.postgresql.org/docs/current/app-psql.html
    accessed: 2026-10-31
    license: "restated"
---
# psql: non-interactive SQL for agents

> **Untested:** iinventedlight has not run the commands in this note. They are restated from the cited docs (checked 2026-10-31). Check them yourself before relying on them.

`psql` was not on PATH (2026-10-31). Steps below come from the PostgreSQL 18 docs only.

## Summary
`psql -c 'SQL'` or `psql -f script.sql` runs SQL and exits; it does not read stdin when those flags are set [S1]. For scripts, tuples-only plus CSV (or unaligned) output is easier to parse than the default aligned table.

## Agent access
- CLI: yes (`psql`). MCP connector: none connected. API: PostgreSQL wire protocol / libpq. GUI: pgAdmin and others, not needed.
- Best route: CLI with `-c`/`-f`, `--no-password`, and `ON_ERROR_STOP`.
- Permissions: a database role that can connect. Writes (INSERT/UPDATE/DDL) need matching grants and the owner's approval. Never print `.pgpass` or connection URIs that embed passwords.

## Steps (from docs, untested)
1. `psql --version` (`-V`) [S1].
2. Read-only one-shot: `psql -X -q --no-password --csv -c "SELECT version();"` [S1].
3. Script file: `psql -X -q --no-password -v ON_ERROR_STOP=1 -f check.sql` [S1].
4. Connection: `-h HOST -p 5432 -U USER -d DB`, or a URI: `psql "postgresql://HOST:5432/DB?sslmode=require"` [S1].
5. List DBs then exit: `psql -l` [S1].

## Traps
- `-c` cannot mix SQL and backslash meta-commands in one string; use two `-c` flags or a file [S1].
- Several SQL statements in one `-c` run as **one** transaction unless the string has explicit BEGIN/COMMIT [S1].
- `-w` / `--no-password` never prompts; the connect fails if a password is required and `.pgpass` is missing [S1].
- On Windows, omitting `-h` uses TCP to `localhost`, not a Unix socket [S1].
- Exit codes: 0 ok, 1 fatal client error, 2 lost connection (non-interactive), 3 script error with `ON_ERROR_STOP` [S1].
- Do not run DROP/TRUNCATE/DELETE without approval naming the database.

## What worked looks like
Exit 0, CSV (or tuples-only) on stdout, no password prompt, no password text in the log.

## Facts
- id: F1
  claim: When -c or -f is given, psql does not read commands from standard input and exits after those options.
  confidence: high
  date: 2026-10-31
  sources: [S1]
  license: CC0 1.0
  checked_on: 2026-10-31
- id: F2
  claim: psql returns exit status 3 when a script errors and ON_ERROR_STOP is set.
  confidence: high
  date: 2026-10-31
  sources: [S1]
  license: CC0 1.0
  checked_on: 2026-10-31
