Scripting com psql
Execute arquivos SQL e verificações parametrizadas em CI, pipelines de implantação e tarefas administrativas repetíveis com o modo não interativo do psql.
Receita
psql "$DATABASE_URL" \
-v ON_ERROR_STOP=1 \
-f migrations/001_init.sql
psql "$DATABASE_URL" -c "SELECT count(*) FROM schema_migrations;"Quando usar isso: Executores de migração, testes de fumaça após a implantação, scripts de seed e playbooks de DBA armazenados no git.
Exemplo de Trabalho
#!/usr/bin/env bash
set -euo pipefail
export PGAPPNAME=ci-schema-check
psql "$DATABASE_URL" \
--single-transaction \
-v ON_ERROR_STOP=1 \
-v tenant_id=42 \
-f scripts/verify_rls.sql \
-o /tmp/verify.out-- scripts/verify_rls.sql
\set ON_ERROR_STOP on
SELECT current_setting('app.tenant_id', true) AS tenant;
SELECT count(*) AS orphan_orders
FROM orders o
WHERE o.tenant_id <> :'tenant_id'::bigint;O que isso demonstra:
-v ON_ERROR_STOP=1aborta o script no primeiro erro SQL (essencial em CI)--single-transactionenvolve o arquivo em um único BEGIN/COMMIT:tenant_idsubstitui variáveis do psql (use:'name'entre aspas para segurança literal)PGAPPNAMEaparece empg_stat_activity.application_name
Mergulho Profundo
Executando Arquivos de Script
| Flag | Propósito |
|---|---|
-f file.sql | Executar arquivo |
\i file.sql | O mesmo, de dentro do psql |
-a | Ecoar toda a entrada para stdout |
-q | Silenciar banners |
-t | Apenas tuplas (sem cabeçalhos) |
-A | Saída não alinhada |
Variáveis
-- Definir no shell: psql -v start_date=2026-01-01
SELECT :'start_date'::date;
-- Solicitar se não estiver definido
\prompt 'Enter tenant id' tenant_id:namesem aspas é substituição de identificador SQL - perigoso para entrada do usuário.:'name'entre aspas é substituição de literal de string - ainda ciente de escape, não uma instrução preparada completa.- Para entrada não confiável, use parâmetros do lado do servidor por meio de código de aplicativo, não variáveis do psql.
Verificações de Fumaça de CI
# .github/workflows/db-smoke.yml (excerto)
- name: Schema smoke test
run: |
psql "${{ secrets.STAGING_DATABASE_URL }}" \
-v ON_ERROR_STOP=1 \
-f ci/assert_indexes.sql-- ci/assert_indexes.sql
SELECT 1/(count(*) > 0)::int AS idx_orders_created_exists
FROM pg_indexes
WHERE tablename = 'orders' AND indexname = 'idx_orders_created_at';- Truque de divisão por zero falha o trabalho quando a contagem é zero.
- Mantenha as verificações idempotentes e rápidas (menos de alguns segundos).
Códigos de Saída
0sucesso1erro fatal (comON_ERROR_STOP)2falha de conexão3erro de script no arquivo-f
Armadilhas
- Falta de ON_ERROR_STOP - O script continua após a instrução falhar; a CI passa com aplicação parcial. Correção: Sempre use
-v ON_ERROR_STOP=1na automação. - Injeção de SQL de variáveis -
:'user_input'em SQL dinâmico de operadores. Correção: Valide o formato; use instruções preparadas na camada do aplicativo para dados do usuário. - Caminhos relativos de
\i- Depende do diretório de trabalho atual quando o psql foi iniciado. Correção: Caminhos absolutos oucdno script wrapper. - COPY em CI sem arquivos -
\copyprecisa de caminho do lado do cliente. Correção: Monte fixtures ou use seedsINSERT. - Scripts longos sem transação única - DDL parcialmente aplicado em caso de falha. Correção: Use
--single-transactionquando todas as instruções forem seguras para transação.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Flyway/Liquibase | Histórico de migração versionado | Script de DBA único |
pg_prove (pgTAP) | Suítes SQL com muitos testes | Consulta rápida de fumaça |
| ORM de migração de aplicativo | Equipe de aplicativo é dona do esquema | Política apenas SQL governada por DBA |
FAQs
psql vs. executar SQL no aplicativo?
O psql se destaca em scripts de operações e verificações de CI. O código do aplicativo deve usar consultas parametrizadas para caminhos voltados para o usuário.
Como passo segredos?
Variável de ambiente DATABASE_URL ou PGPASSWORD no armazenamento de segredos da CI - nunca envie credenciais no commit.
O psql pode executar meta-comandos do psql em arquivos -f?
Sim - \i, \set, \connect funcionam em arquivos de script. Eles são do lado do cliente, não enviados ao servidor.
Por que a CI passou, mas o staging está quebrado?
Provavelmente sem ON_ERROR_STOP ou as verificações foram executadas contra a URL do banco de dados errada. Adicione \conninfo no topo dos scripts.
Relacionados
- Fundamentos do psql - fundamentos interativos
- Fundamentos de Migrações - SQL versionado no git
- Flyway & Liquibase - executores empresariais
Versões da pilha: Esta página foi escrita para PostgreSQL 18.4 (estável 18, manutenção 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x e PostGIS 3.5+.