Categoria: PostgreSQL

  • Meu Postgres não conecta: troubleshooting de conexões

    Meu Postgres não conecta: troubleshooting de conexões

    Introdução

    Nesse artigo, vamos explorar problemas comuns ao tentar se conectar com seu servidor PostgreSQL. Há diversas possibilidades de problemas enfrentados, então veremos cenários mais comuns e como solucioná-los da maneira mais simples e rápida possível.

    Roteiro:

    • O postgres está no ar?
    • Temos rota para conectar?
    • O pg_hba.conf e o postgresql.conf estão conectados devidamente?
    • Eu tenho permissão para me conectar?
    • Meu usuário existe?
    • O banco de dados existe?
    • A senha está correta?


    Verifique sempre os logs da sua aplicação e, se possível, tente estabelecer uma conexão com o banco de dados utilizando o psql, que é o client oficial do PostgreSQL. Faça isso para eliminar qualquer possibilidade de erro em outra camada da sua aplicação e para poder ver claramente os erros que acontecem durante a conexão. Claro que para isso você vai precisar instalar o client do PostgreSQL no seu servidor de aplicação ou desktop, dependendo do caso. Sempre teste a conexão a partir do mesmo lugar da aplicação. Dependendo da origem da conexão, rotas, firewalls, VPNs, entre outras variáveis na sua rede podem mudar. Vamos mostrar aqui vários exemplos de erros que acontecem durante a conexão e simulá-los sempre utilizando o psql.

    Recebi uma mensagem dizendo que o banco de dados não está no ar!

    Na maioria das vezes, o erro que você vai receber quando tem um problema de conexão é este aqui:

    psql shell: mensagem de erro por banco de dados fora do ar
    psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory
            Is the server running locally and accepting connections on that socket?


    Apesar de a mensagem dar a entender que o seu banco de dados não está no ar, o motivo desse aviso pode variar um pouco:

    1. Você pode ter digitado o endereço do servidor ou a porta incorretamente;
    2. Você pode não ter uma rota na rede até o servidor;
    3. Pode haver um firewall bloqueando o caminho até o servidor;
    4. O banco de dados pode realmente não estar no ar; 
    5. O parâmetro listen_address pode estar desativado para esse IP.

    A primeira coisa a fazer numa tentativa falha de conexão com o banco de dados, como a exibida anteriormente, é checar se o serviço do banco está disponível, ou seja, no ar. 

    É possível verificar o status desse serviço com diversos comandos, que já abordamos por aqui, mas, para relembrar, veja alguns deles (se executados como usuário root, pode ser necessário utilizar o comando sudo antes, caso esteja logado com outro usuário):

    • ps -faux | grep postgres (este método é o mais confiável e funciona para qualquer ambiente Linux/Unix):
    Debian shell: processos postgres
    # ps -faux | grep postgres
    root     1820618  0.0  0.1   3876  1832 pts/0    S+   14:41   0:00          \_ grep postgres
    999      3358569  0.0  1.4 220064 14652 ?        Ss   Jul23   7:16  \_ postgres
    999      3358637  0.0  3.1 220352 31392 ?        Ss   Jul23   0:05      \_ postgres: checkpointer
    999      3358638  0.0  0.7 220216  7452 ?        Ss   Jul23   1:07      \_ postgres: background writer
    999      3358640  0.0  0.7 220192  6904 ?        Ss   Jul23   1:13      \_ postgres: walwriter
    999      3358641  0.0  0.4 221656  4024 ?        Ss   Jul23   1:45      \_ postgres: autovacuum launcher
    999      3358642  0.0  0.4 221644  4020 ?        Ss   Jul23   0:03      \_ postgres: logical replication launcher
    999        82547  0.0  1.0  11828 10184 ?        Sl   Aug20   4:02      \_ postgres:
    999      1800988 99.5 27.7 308912 272700 ?       Sl   Oct16 1170:12          \_ postgres:


    Caso o banco não esteja no ar, não aparecerão os diversos processos do serviço postgres, aparecendo, em geral, apenas o processo da própria busca do comando grep.

    • systemctl status postgresql (funciona para sistemas Linux modernos que usam o systemd configurados corretamente com o PostgreSQL. Porém, nos sistemas baseados em Red Hat, é necessário colocar -<número_versão> após postgresql, por exemplo: systemctl status postgresql-13): 
    Debian shell: consulta do status do serviço postgres
    # systemctl status postgresql
     postgresql.service - PostgreSQL RDBMS
         Loaded: loaded (/lib/systemd/system/postgresql.service; enabled; preset: enabled)
         Active: active (exited) since Tue 2024-04-09 18:41:03 UTC; 5 months 0 days ago
        Process: 439735 ExecReload=/bin/true (code=exited, status=0/SUCCESS)
       Main PID: 171728 (code=exited, status=0/SUCCESS)
            CPU: 1ms
    
    Sep 04 19:32:13 debian-teste systemd[1]: Reloading postgresql.service - PostgreSQL RDBMS...
    ...

    Se o banco de dados estiver fora do ar, a seção ‘Active’ exibirá ‘inactive (dead)’, também indicando desde quando o serviço está nesse estado, conforme o exemplo. Há a possibilidade de o comando systemctl não estar disponível, se o servidor foi inicializado sem o systemd, e se for esse o caso, deverão ser utilizadas as outras alternativas demonstradas.

    Uma última observação importante é que se o PostgreSQL estiver sendo manipulado sem o comando systemctl, pode ser que este método apresente informações incorretas, portanto, pode não ser uma boa ideia confiar nele.

    • pg_lsclusters (para sistemas baseados em Debian):
    Debian shell: consulta do serviço postgres
    # pg_lsclusters
    Ver Cluster     Port Status Owner    Data directory                     Log file
    16  main        5432 online postgres /var/lib/postgresql/16/main        /var/log/postgresql/postgresql-16-main.log
    16  novocluster 5433 online postgres /var/lib/postgresql/16/novocluster /var/log/postgresql/postgresql-16-novocluster.log


    Aqui, se o serviço estiver fora do ar, aparecerá ‘down’ no lugar de ‘online’, com as letras geralmente na cor vermelha, para indicar que não está funcionando. 

    • pg_ctlcluster <sua_versao> <nome_cluster> status (apenas para sistemas baseados em Debian):
    Debian shell: consulta do serviço postgres
    # pg_ctlcluster 16 main status
    pg_ctl: server is running (PID: 433805)
    /usr/lib/postgresql/16/bin/postgres "-D" "/var/lib/postgresql/16/main" "-c" "config_file=/etc/postgresql/16/main/postgresql.conf"


    No caso de o banco estar fora do ar, o retorno será ‘pg_ctl: no server running’.

    • sc query postgresql-x64-<número_versão> (para Windows):
    Windows shell: consulta do serviço postgres
    C:\Windows\System32>sc query postgresql-x64-16
    
    NOME_DO_SERVIÇO: postgresql-x64-16
        TIPO                       : 10  WIN32_OWN_PROCESS
        ESTADO                     : 4  RUNNING
                                    (STOPPABLE, PAUSABLE, ACCEPTS_SHUTDOWN)
        CÓDIGO_DE_SAÍDA_DO_WIN32   : 0  (0x0)
        CÓDIGO_DE_SAÍDA_DO_SERVIÇO : 0  (0x0)
        PONTO_DE_VERIFICAÇÃO       : 0x0
        AGUARDAR_DICA              : 0x0

    Aqui, a mudança para o banco fora do ar seria no ‘ESTADO’, diferente de ‘RUNNING’.

    Como você pode ver, nos nossos exemplos, todos os comandos retornaram resposta positiva quanto ao estado do serviço do banco, que está no ar. Mas e se não estivesse, o que poderia ser feito? Como já citamos em artigos anteriores, no Linux é possível gerenciar o estado do seu banco de dados também com o utilitário systemctl:

    • Para subir, poderíamos utilizar o comando (que não tem saída):
      systemctl start postgresql

      E assim checar novamente o status do banco por um dos meios mencionados anteriormente.

      Em Windows, o comando para iniciar é: net start postgresql-x64-16 


    Observação: se você iniciou o serviço, verifique se ele realmente subiu. Se não subir, verifique os logs do PostgreSQL para entender o motivo. Se o banco de dados estiver no ar, verifique novamente se a conexão funciona, primeiro localmente, depois remotamente.

    Temos rota para conectar?

    Depois de checar o status do serviço do PostgreSQL, caso ainda não tenha conseguido conectar, pode ser interessante checar a rota de conexão com o banco de dados. 
    Algumas das mensagens de erros comuns para esse tipo de situação são:

    psql shell: mensagem de erro por conexão recusada
    psql: error: could not connect to server: Connection refused Is the server running on host "localhost" (127.0.0.1) and accepting TCP/IP connections on port 5432?
    psql shell: mensagem de erro por conexão falha
    psql: error: connection to server at "192.168.0.1", port 5432 failed: Connection timed out        
    Is the server running on that host and accepting TCP/IP connections?

    Podemos conferir o status da rota consultando a conectividade de rede entre o cliente e o servidor PostgreSQL por meio de algumas ferramentas comuns:

    • Ping: verifica se o servidor está acessível a partir do cliente. O comando ping <ip-do-servidor> pode ser usado para verificar a resposta do servidor. Se o servidor não responder, isso pode indicar problemas de rede, como bloqueio por firewall ou roteamento incorreto. O comando ping está disponível tanto em Windows quanto em Linux e funciona de forma similar. É um bom comando para ver se um servidor está no ar, mesmo sem saber a porta que será utilizada. No entanto, alguns servidores têm o “ping” bloqueado intencionalmente por motivos de segurança, por isso ele nem sempre é a forma mais recomendada de verificar se o servidor remoto está acessível. 
    Debian shell: teste de conexão via comando ping
    $ ping 159.89.241.130
    PING 159.89.241.130 (10.0.0.240) 56(84) bytes of data.
    64 bytes from 159.89.241.130: icmp_seq=1 ttl=64 time=0.081 ms
    64 bytes from 159.89.241.130: icmp_seq=2 ttl=64 time=0.127 ms
    64 bytes from 159.89.241.130: icmp_seq=3 ttl=64 time=0.063 ms
    64 bytes from 159.89.241.130: icmp_seq=4 ttl=64 time=0.058 ms
    64 bytes from 159.89.241.130: icmp_seq=5 ttl=64 time=0.054 ms
    64 bytes from 159.89.241.130: icmp_seq=6 ttl=64 time=0.062 ms
    • Porta: em todos os demais exemplos, você vai precisar saber em qual porta o PostgreSQL está rodando. A porta padrão é a 5432, mas ela pode ser substituída por outra conforme a necessidade. Uma maneira confiável de verificar a porta em uso é conectar-se ao banco de dados localmente via psql e executar o comando SHOW port:
    psql shell: consulta de porta
    postgres=# SHOW port;
     port
    ------
     5432
    (1 row)

    No caso de sistemas baseados em Debian, também é possível usar o comando pg_lsclusters:

    Debian shell: consulta de porta
    # pg_lsclusters
    Ver Cluster Port Status Owner    Data directory              Log file
    16  main    5432 online postgres /var/lib/postgresql/16/main /var/log/postgresql/postgresql-16-main.log
    • Nmap: o Nmap foi desenvolvido na década de 1990 como uma forma de descobrir serviços e portas utilizadas numa rede. Se você não tiver acesso local ao servidor, para verificar qual é a porta utilizada, ou se o seu administrador de redes estiver indisponível, você pode utilizar o Nmap, que faz isso para você. Alguns administradores podem não gostar muito de alguém varrendo a rede local e cutucando de porta em porta do servidor para saber se alguma está aberta. Existem ambientes com defesas contra esse tipo de ação, por ser utilizada por pessoas com más intenções querendo invadir um servidor. Esteja avisado!
    Debian shell: retorno do comando nmap
    $ nmap 159.89.241.130
    Starting Nmap 7.93 ( https://nmap.org ) at 2024-12-11 15:10 -03
    Nmap scan report for 159.89.241.130
    Host is up (0.12s latency).
    Not shown: 998 closed tcp ports (conn-refused)
    PORT     STATE    SERVICE
    3/tcp    filtered compressnet
    22/tcp   open     ssh
    5432/tcp open     postgresql
    
    Nmap done: 1 IP address (1 host up) scanned in 14.55 seconds
    • Telnet: é um protocolo desenvolvido na década de 1960, que embora ninguém mais pense em usar com a sua finalidade original, é utilizado por algumas pessoas  para testar conexões entre computadores, por ser amplamente conhecido e fácil de usar, estando disponível em qualquer sistema operacional. O comando telnet <ip-do-servidor> 5432 pode ser aplicado para testar se a porta está acessível. Se a conexão for bem-sucedida, você verá uma mensagem confirmando a conexão (antes, pode ser necessário instalar o pacote do Telnet). Provavelmente, o Telnet não estará instalado por padrão. Você também pode utilizar as próximas opções da nossa lista. Recomendamos!
    Debian shell: retorno do comando telnet
    $ telnet 159.89.241.130 5432
    Trying 159.89.241.130...
    Connected to 159.89.241.130.
    Escape character is '^]'.
    • Netcat: similar ao Telnet, o comando nc -vz <ip-do-servidor> 5432 pode ser usado para verificar se a porta está aberta e acessível (também pode ser necessário instalar o pacote antes):
    Debian shell: retorno do comando netcat
    $ nc -vz 159.89.241.130 5432
    Connection to 159.89.241.130 5432 port [tcp/postgresql] succeeded!

    Não tenho rota para o servidor, pode ser um firewall no caminho

    Se você sabe que o PostgreSQL está no ar, sabe em qual porta ele roda, usou um dos aplicativos citados para tentar verificar e mesmo assim não conseguiu chegar ao banco de dados, você provavelmente está sendo bloqueado por alguma regra de segurança do firewall.

    Para verificar se o firewall local está ativo, podemos utilizar os seguintes comandos:

    • Para sistemas baseados em Debian/Ubuntu (usando UFW): sudo ufw status
    • Para sistemas baseados em Red Hat/CentOS (usando firewalld): sudo firewall-cmd --list-all


    Aqui, a resposta pode ser firewall ativo ou não (active/inactive), e caso não esteja ativo, é improvável que ele cause alguma dificuldade de conexão. Se estiver ativo, podemos ainda checar se a porta do lado do servidor está aberta com os comandos a seguir:

    • Para sistemas baseados em Debian/Ubuntu (usando UFW):
      • Verificar a porta: sudo ufw status | grep 5432
    • Abrir a porta (se necessário): sudo ufw allow 5432/tcp

    • Para sistemas baseados em Red Hat/CentOS (usando firewalld):
      • Verificar: sudo firewall-cmd --list-ports | grep 5432
    • Abrir:
      sudo firewall-cmd --zone=public --add-port=5432/tcp --permanent
      sudo firewall-cmd --reload


    Geralmente, não é necessário abrir portas específicas no firewall do cliente para conexões de saída. No entanto, se houver restrições, você pode precisar permitir conexões de saída para a porta 5432:

    • Para sistemas baseados em Debian/Ubuntu (usando UFW):
      sudo ufw allow out to any port 5432
    • Para sistemas baseados em Red Hat/CentOS (usando firewalld):
      sudo firewall-cmd --zone=public --add-port=5432/tcp --permanent
      sudo firewall-cmd --reload

    E se o firewall não for local?

    Vejamos:

    • O banco de dados está no ar;
    • Você sabe a porta correta;
    • Não existe nenhum firewall local ativo;
    • Ainda assim você não tem rota até o banco de dados.


    Nesse momento, pode haver algum problema de rota, firewall etc. no meio do caminho. Você vai precisar conversar com o administrador de redes para ajudar nisso. Foge um pouco do escopo deste artigo entrar nesses meandros, pois existem muitas variáveis que dependem da arquitetura da rede, da segurança, do uso de VPN etc. 

    O pg_hba.conf e o postgresql.conf estão configurados devidamente?

    Conforme abordado no último artigo, o pg_hba.conf é o arquivo que dita as regras de autenticação do banco de dados, o que significa que a sua configuração correta impacta totalmente em uma conexão bem-sucedida. Caso tenha algum problema ou impedimento nessas configurações, você deverá receber um retorno como este:

    psql shell: mensagem de erro no pg_hba.conf
    psql: error: connection to server at "192.168.0.1" (::1), port 5432 failed: FATAL:  no pg_hba.conf entry for host "::1", user "postgres", database "postgres", SSL off


    Certificar-se de que o seu usuário e IP têm permissão de conexão no banco de dados correto no arquivo é um passo fundamental em caso de erro no acesso remoto, bem como seu usuário (ou grupo de usuários).

    Além disso, no arquivo de configurações do PostgreSQL, o postgresql.conf, existe um parâmetro que é imprescindível para permitir conexões externas, o listen_addresses, que por padrão vem com o valor de localhost, significando que o servidor escuta do endereço local. É possível fazer restrições aqui, para maior segurança, podendo permitir IPs específicos, por exemplo:

    postgresql.conf: definindo valores para parâmetro listen_adresses
    listen_addresses = '192.168.1.100, localhost'

    Então é necessário ter certeza de que está autorizado nas regras de ambos os arquivos.
    Caso haja algum ajuste para permitir a conexão, o PostgreSQL deverá ser recarregado ou reiniciado, para que as novas configurações sejam aplicadas. Isso pode ser feito com os seguintes comandos:

    • Para recarregar (sem interromper conexões atuais, o suficiente para mudanças no pg_hba.conf): systemctl reload postgresql
    • Para reiniciar o banco (caso tenha alterado o parâmetro listen_addresses): systemctl restart postgresql


    O banco de dados existe?

    Outra possível causa de falha de conexão é o banco de dados que você está tentando acessar não existir. Nessa situação, temos a seguinte mensagem de erro:

    psql shell: mensagem de erro sobre banco de dados inexistente
    psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL:  database "banco1" does not exist


    Caso tenha esse retorno e queira verificar se digitou corretamente o nome do banco de dados ou se ele está presente no servidor apontado, você pode verificar isso localmente com um terminal psql (como usuário administrador) e listar todos os bancos de dados disponíveis com o comando \l. Esse e outros comandos básicos do psql podem ser relembrados no artigo Utilização do psql.

    Esta é a nossa listagem de bancos:

    psql shell: listando todos os bancos de dados
    postgres=# \l
                                                        List of databases
        Name    |  Owner   | Encoding | Locale Provider | Collate |  Ctype  | ICU Locale | ICU Rules |   Access privileges
    ------------+----------+----------+-----------------+---------+---------+------------+-----------+-----------------------
     aplicacao1 | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           |
     postgres   | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           |
     template0  | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           | =c/postgres          +
                |          |          |                 |         |         |            |           | postgres=CTc/postgres
     template1  | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           | =c/postgres          +
                |          |          |                 |         |         |            |           | postgres=CTc/postgres
    (4 rows)


    Se o banco de dados que você está tentando acessar estiver listado, ele existe. Caso contrário, se necessário, você pode criá-lo com o comando CREATE DATABASE:

    psql shell: comando para criar nova base de dados
    CREATE DATABASE <nome_do_banco> OWNER <nome_do_usuario>;

    Meu usuário existe?

    Outra questão fundamental na hora de resolver problemas de conexão é garantir que o usuário que está tentando se conectar ao PostgreSQL realmente existe no banco de dados. Se o usuário não estiver devidamente criado ou não tiver as permissões corretas, a conexão falhará.

    A mensagem de erro para essa ocasião é:

    psql shell: mensagem de erro sobre usuário inexistente
    psql: error: FATAL: role "my_username" does not exist

    Para verificar se o usuário existe, você pode utilizar o comando SQL \du no terminal interativo psql, que lista todos os usuários (também chamados de “roles” no PostgreSQL):

    psql shell: consultando todos os usuários
    postgres=# \du
                                    List of roles
        Role name    |                         Attributes
    -----------------+------------------------------------------------------------
     admin_pagila    | Create role
     ana             |
     app1_admin      | Create role, Create DB
     app1_user1      |
     app1_user2      |
     app2_admin      | Create role, Create DB
     app2_user1      |
     app2_user2      |
     dba             | Superuser
     fabio           |
     ludmila         |
     meu_usuario     | Superuser
     postgres        | Superuser, Create role, Create DB, Replication, Bypass RLS


    Se o seu usuário estiver nessa lista, ele existe. Caso não esteja, você precisará criá-lo com o comando CREATE ROLE, que pode ser visto com detalhes no artigo Utilização de SQL básico:

    psql shell: comando para criar um novo usuário, com permissão de login e definição de senha
    CREATE ROLE <nome_do_usuário_ou_grupo> LOGIN PASSWORD '<senha>';

    Não se esqueça de garantir as permissões necessárias para que esse usuário possa se conectar.

    Eu tenho permissão para me conectar?

    Mesmo se a configuração no pg_hba.conf e postgresql.conf estiver correta, o seu usuário existir e o banco de dados também, você ainda poderá encontrar outros problemas. É possível que o usuário não tenha privilégios suficientes para realizar a conexão ou para acessar objetos específicos dentro do banco. Se for o caso de não haver permissão para login, você deverá ver uma mensagem parecida com esta:

    psql shell: mensagem de erro relacionada a falta de permissão de login
    psql: error: connection to server on at 192.168.0.10 failed: FATAL:  role "fabio" is not permitted to log in


    Isso pode ser facilmente resolvido concedendo esse privilégio com o comando:

    ALTER ROLE <usuário ou grupo> WITH LOGIN;

    Agora, se houver falta de permissão para se conectar com o banco, essa deverá ser a mensagem de erro visualizada:

    psql shell: mensagem de erro relacionada a falta de permissão de conexão com o banco
    psql: error: connection to server at 192.168.0.10, port 5432 failed: FATAL: permission denied for database "postgres"
    DETAIL: User does not have CONNECT privilege.


    Podemos resolver isso com o comando GRANT, que vimos detalhadamente no artigo O mínimo sobre segurança que você precisa saber. Em um resumo rápido, existe o privilégio CONNECT, que especificamente trata de permissão de conexão com o banco, que pode ser fornecido com o seguinte comando:

    GRANT CONNECT ON DATABASE <nome do banco> TO <usuario>;

    Isso e muito mais pode ser visto de forma detalhada no artigo citado.

    A senha está correta?

    Mesmo que o banco de dados e o usuário estejam configurados corretamente, uma senha incorreta pode ser um obstáculo. Se você suspeita que a senha do seu usuário está errada ou precisa ser redefinida, há algumas formas de corrigir isso. Este é o erro para esse caso:

    psql shell: mensagem de erro para senha incorreta
    psql: error: connection to server at "localhost" (::1), port 5432 failed: FATAL:  password authentication failed for user "user3"


    No PostgreSQL, não há um comando direto para visualizar senhas, já que elas são armazenadas de forma segura. No entanto, se o arquivo pg_hba.conf estiver configurado para um método de autenticação que exija senha (como md5 ou scram-sha-256), a senha deve estar correta para que a conexão seja estabelecida.

    Caso precise alterar a senha de um usuário, você pode fazê-lo através do psql, com um usuário com permissões de superusuário, executando o comando ALTER USER para realizar a alteração:

    psql shell: comando para alterar a senha de um usuário
    ALTER USER <nome_do_usuario> WITH PASSWORD '<nova_senha>';

    Agora, a nova senha será usada nas próximas tentativas de conexão. Se o problema estava sendo causado por uma senha incorreta, ele será resolvido.

    Conclusão

    Quando você instala o PostgreSQL pela primeira vez em um servidor, é fundamental configurar o postgresql.conf e o pg_hba.conf corretamente, uma vez que a instalação padrão não vem configurada para permitir conexões remotas. Depois disso, você talvez precise criar novos usuários, bancos de dados, senhas etc. Por fim, a rede onde você roda o PostgreSQL deve permitir o acesso remoto ao servidor naquela porta. 

    Além disso, muitas coisas podem dar errado nesse caminho. Não caia na tentação de burlar os mecanismos de segurança! Se você entender o significado de cada mensagem de erro que apresentamos aqui, rapidamente você conseguirá chegar lá. Mas lembre-se: sempre verifique o que a mensagem de erro diz, pois isso será fundamental para entender onde e como agir para resolver o problema. 

  • pg_hba.conf

    pg_hba.conf

    Introdução

    O arquivo pg_hba.conf (PostgreSQL Host-Based Authentication) é uma peça fundamental na configuração de segurança de qualquer instância PostgreSQL. Ele define as regras de autenticação e acesso ao banco de dados, determinando quem pode se conectar, de onde e como essas conexões são autenticadas. Neste artigo, exploraremos as diversas opções de configuração disponíveis no pg_hba.conf, desde os métodos de autenticação até a especificação de IPs, usuários e grupos. Também abordaremos os modelos de autenticação mais comuns, como TRUST, password, md5 e scram-sha-256, além de métodos específicos como ident e peer. Ao final, você será capaz de configurar esse arquivo para garantir um ambiente seguro e eficiente para suas aplicações.

    Roteiro:

    • Modelo de autenticação
      • TYPE (tipos de conexão)
        • local
        • host
        • hostssl
      • DATABASE (banco de dados)
      • USER (usuários ou grupos de usuários)
      • ADDRESS (endereços)
      • METHOD (método)
      • OPTIONS (opções)
      • Exemplos
      • Considerações importantes
    • Ident file

    Modelo de autenticação

    O modelo de autenticação define como a autenticação será realizada e gerenciada, verificando a identidade dos usuários do banco de dados de diferentes formas. No PostgreSQL, o principal modelo é baseado no arquivo pg_hba.conf. Ele pode estar localizado em diferentes locais, dependendo do seu ambiente:

    • /var/lib/pgsql/<versão>/data/ nos ambientes Linux Red Hat e derivados (como CentOS);
    • /etc/postgresql/<versão>/<cluster>/pg_hba.conf nos ambientes Linux Debian e derivados (como Ubuntu);
    • C:\Program Files\PostgreSQL\<versão>\data\pg_hba.conf em Windows;
    • Se você criou o seu cluster de dados em um diretório específico e não está utilizando o Linux Debian, o pg_hba.conf deverá estar nesse diretório, que chamamos de $PGDATA (que é a variável de ambiente que contém essa localização).

    O pg_hba.conf tem como características regras específicas que suportam vários métodos de autenticação, que podem ainda ser aplicados de forma seletiva.

    A estrutura básica do pg_hba.conf é (em cada linha):

    Arquivo pg_hba.conf: estrutura padrão
    TYPE  DATABASE  USER  ADDRESS  METHOD [OPTIONS]

    Sendo que  TYPE se refere ao tipo de conexão, DATABASE, ao banco de dados  ao qual a regra se aplica, USER, a usuário ou grupo de usuários, ADDRESS, ao endereço IP ou intervalo (range) de IPs, METHOD  ao método de autenticação, e OPTIONS a algumas opções que podem existir de acordo com o método escolhido, respectivamente.
    Além disso, no pg_hba.conf, as linhas de regras são divididas por seções, sendo uma para conexões locais, outra para IPv4, outra para IPv6 e mais uma para replicações.
    Apenas para lembrar, é tradição no universo Linux/Unix (de onde o PostgreSQL surgiu) usar o caractere ‘#‘ para marcar comentários no início da linha.

    Confira um exemplo de conteúdo do pg_hba.conf:

    Arquivo pg_hba.conf: exemplo de conteúdo
    # DO NOT DISABLE!
    # If you change this first entry you will need to make sure that the
    # database superuser can access the database using some other method.
    # Noninteractive access to all databases is required during automatic
    # maintenance (custom daily cronjobs, replication, and similar tasks).
    #
    # Database administrative login by Unix domain socket
    local   all             postgres                                peer
    
    # TYPE  DATABASE        USER            ADDRESS                 METHOD
    
    # "local" is for Unix domain socket connections only
    local   all             all                                     peer
    # IPv4 local connections:
    host    all             all             127.0.0.1/32            scram-sha-256
    # IPv6 local connections:
    host    all             all             ::1/128                 scram-sha-256
    # Allow replication connections from localhost, by a user with the
    # replication privilege.
    local   replication     all                                     peer
    host    replication     all             127.0.0.1/32            scram-sha-256
    host    replication     all             ::1/128                 scram-sha-256

    TYPE (tipos de conexão)

    O PostgreSQL suporta diversos tipos de conexão, cada um com suas próprias características e casos de uso, que determinam como os clientes podem se conectar ao servidor de banco de dados. Entre os principais e mais utilizados, estão:

    • local: conexões feitas através de Unix sockets no mesmo sistema onde o servidor PostgreSQL está rodando, ou seja, conexões locais, portanto, não sendo necessário passar a informação da coluna ADDRESS;
    • host: conexões TCP/IP tanto locais quanto remotas não criptografadas, ou seja, sem criptografia SSL;
    • hostssl: para conexões TCP/IP seguras, ou seja, criptografadas via SSL. É o que recomendamos utilizar e teremos um artigo especial dedicado a ele.

    Existem ainda as opções mais incomuns: hostnossl (sem SSL), hostgssenc (com criptografia GSSAPI) e hostnogssenc (sem criptografia GSSAPI).

    DATABASE (banco de dados)

    Para isolar as regras de autenticação, deve-se especificar a qual banco a linha com as instruções se refere, sendo possível utilizar all para aplicação em todos os bancos. Além disso, há a opção samerole, que é particularmente útil em ambientes com multitenancy, onde diferentes usuários ou grupos precisam acessar seus próprios bancos de dados sem interferir em outros. Com samerole, o Postgres permite que um usuário se conecte apenas a bancos de dados cujo nome coincide com uma role (grupo) da qual ele é membro. Assim, é possível definir que cada usuário tenha acesso apenas aos bancos relacionados ao seu papel, reforçando a segurança e o isolamento entre bancos de dados de diferentes usuários.

    USER (usuários ou grupos de usuários)

    É possível especificar usuários individuais do PostgreSQL no pg_hba.conf pelo campo USER ou, ainda, usar all para corresponder a todos os usuários, assim como para os bancos de dados.
    Podemos também indicar grupos de usuários com o prefixo +, por exemplo, +admins, o que torna fácil o processo de gerenciar permissões de vários usuários simultaneamente. É importante lembrar que não são grupos do sistema operacional, mas, sim, roles definidas no PostgreSQL.
    No contexto do PostgreSQL, um “grupo de usuários” é, na verdade, uma role à qual outras roles (usuários) recebem grants (privilégios). Isso permite agrupar vários usuários sob uma mesma role, facilitando o gerenciamento de permissões e acessos em ambientes mais complexos.
    Além disso, também podemos informar nomes de usuário como expressões regulares, indicando com o prefixo /, e também é possível gerenciar os usuários, com nome ou expressões regulares, em um arquivo separado, que deve ser mencionado com o prefixo @. Há ainda o caso de replicação, que trata-se de uma conexão usada para sincronizar dados entre servidores, e que deve ser indicada nessa coluna com a palavra-chave replication.

    ADDRESS (endereços)

    O campo ADDRESS no pg_hba.conf permite especificar quais endereços podem se conectar:

    • Subnet: permite conexões de uma faixa de IPs, por exemplo:
      • 192.168.1.1/32 ou fe80::7a31:c1ff:fa34:9eb3/128 – permite apenas um IP específico;
      • 127.0.0.1 ou ::1/128 - (ipv6) – permite apenas conexões locais;
      • 192.168.1.0/24 – permite todos os IPs classe C, de 192.168.1.0 a 192.168.1.255;
      • 192.168.0.0/16 – permite todos os IPs classe B, de 192.168.0.0 a 192.168.255.255;
      • 192.0.0.0/8 – permite todos os IPs classe A, de 192.0.0.0 a 192.255.255.255;
      • 0.0.0.0/0 ou ::0/0 (ipv6) – permite qualquer IP (não recomendamos usar esta configuração em ambiente de produção).
    • IP-mask: permite a definição de uma faixa de IPs, semelhante a anterior, mas em vez de usar a subnet, colocamos o IP e a máscara de IPs:
      • 192.168.1.0 ou 255.255.255.0 – permite todos os IPs classe C, de 192.168.1.0 a 192.168.1.255;
      • 192.168.0.0 ou 255.255.0.0 – permite todos os IPs classe B, de 192.168.0.0 a 192.168.255.255.
    • Hostname: além de endereços IP, podemos utilizar um hostname, no qual o PostgreSQL realiza várias verificações para garantir a correspondência entre o hostname especificado e o cliente. Lembrando que qualquer lentidão na resolução de nomes pelo DNS irá provocar uma lentidão no processo de conexão em si.
      • Hostname específico: é só colocá-lo, como exemplo.com;
      • Subdomínios: necessário colocar o prefixo “.” antes do hostname, e assim qualquer endereço com esse subdomínio será autorizado, como em .exemplo.com permitiria conexão de sub.exemplo.com, app.exemplo.com etc. (mas não o próprio exemplo.com).
    • Também podemos utilizar all para permitir a conexão de qualquer IP, samehost para permitir qualquer IP do próprio servidor e samenet para permitir qualquer IP em qualquer sub-rede à qual o servidor esteja conectado.

    METHOD (método)

    Os métodos de autenticação no PostgreSQL são as diferentes maneiras que o banco de dados usa para verificar e realizar a autenticação, indo desde a exigência de senhas até a confiança total e permissão absoluta. Os principais são:

    • trust: permite conexões sem nenhuma autenticação, ou seja, se o seu IP for contemplado na lista de endereços autorizados, não será necessário fornecer senhas, por isso é adequado apenas para ambiente de testes e desenvolvimento local ou com redes totalmente isoladas e confiáveis endereçando apenas um IP local ou um IP único confiável;
    • password / md5 / scram-sha-256: é exigida uma senha no momento da conexão, que varia entre essas três opções com diferentes níveis de segurança:
      • password: senha em texto simples, sem criptografia durante a transmissão pela rede;
      • md5: usa hash MD5 para criptografar a senha durante a transmissão, o que torna o processo um pouco mais seguro, mas seu uso está sendo descontinuado em favor do próximo método considerado mais seguro;
      • scram-sha-256: usa o método SCRAM (Salted Challenge Response Authentication Mechanism) como hash SHA-256, o que o torna a opção baseada em senha mais segura, já que seu protocolo de autenticação não transmite a senha real, e sua criptografia é considerada muito forte.

    É importante saber que o tipo de encriptação padrão da senha (md5 ou scram-sha-256) é definido pelo parâmetro password_encryption, que significa que todos os seus usuários criados com senha, ou com a senha alterada, vão tê-la com a criptografia apontada por esse parâmetro. O nosso padrão é scram-sha-256, porém fizemos alterações para exemplificar diferentes cenários;

    • ident: usa o serviço de identificação do sistema operacional (Ident ou identd) para autenticar, confiando na informação que o SO passa, por isso requer que o cliente esteja em um servidor ident (mais comum em ambientes Linux, em que o serviço Ident já vem ativado por padrão); 
    • peer: confia nas informações de usuários do SO local, ou seja, diferente do ident, não precisa de um serviço externo para isso e só funciona para redes locais. É esse o método de autenticação que costumamos usar para acessar o psql com usuário postgres (estando conectado ao usuário postgres do SO);
    • Além desses, o PostgreSQL suporta vários outros métodos, incluindo:
      • reject: rejeita todas as conexões, podendo ser usado para filtrar e impedir grupos; 
      • sspi: autenticação nativa do Windows;
      • ldap: para autenticação via servidores LDAP;
      • radius: para autenticação via servidores RADIUS;
      • cert: usa certificados SSL do cliente para autenticação.

    OPTIONS (opções)

    Além de todas as configurações que vimos, é possível ainda inserir mais opções para o método de autenticação, que devem ter o formato de nome=valor, e servem para especificar opções adicionais para o método escolhido. Algumas delas são:

    • clientcert: além das opções específicas de cada método de autenticação, existe essa opção geral, que pode ser usada em qualquer entrada do tipo hostssl. Essa opção tem dois valores possíveis: verify-ca e verify-full, sendo que o primeiro exige que o cliente apresente um certificado SSL válido e confiável, e o segundo, além de exigir um certificado válido, também verifica se o Common Name (CN) no certificado do cliente corresponde ao nome de usuário ou a um mapeamento aplicável. Isso fornece um nível extra de segurança;
    • clientname: quando você está usando autenticação com certificado de cliente (usando o método cert ou a opção clientcert), você pode usar essa opção para especificar qual parte das credenciais do certificado do cliente deve ser comparada com o nome de usuário, sendo as subopções clientname=CN, em que o nome de usuário será comparado com o Common Name (CN) do certificado, e clientname=DN, em que o nome de usuário será comparado com o Distinguished Name (DN) completo do certificado, conforme o formato RFC 2253;
    • include, include_if_exists e include_dir: essas opções permitem definir suas regras de autenticação em um arquivo separado, sendo necessário passar o local do arquivo entre aspas simples. A diferença entre essas opções é que a primeira apenas checa o local do arquivo informado e tenta substituir as regras, a segunda faz isso apenas se o arquivo existir, caso contrário será exibida uma mensagem indicando que o arquivo não foi lido, e a última reconhece todos os arquivos de um diretório.

    Exemplos

    Conexão local de todos os usuários com peer

    Arquivo pg_hba.conf: exemplo de configuração de conexão
    local		all		all		peer

    Este é o caso que possibilita o acesso ao psql pelo usuário postgres do SO, pulando a etapa de fornecimento de senha (acessamos o banco também chamado postgres):

    Debian shell: exemplo de acesso ao psql sem inserção de senha
    postgresdebian:~$ psql -U postgres -d postgres
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    Type "help" for help.
    
    postgres=#

    Além desse usuário, poderíamos repetir esse tipo de acesso com qualquer outro usuário que existisse tanto no banco de dados quanto no SO, claro, com o mesmo nome nos dois lugares:

    Debian shell: acessando o psql sem inserção de senha
    ludmila@debian:~$ psql -U ludmila -d postgres
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    Type "help" for help.
    
    postgres=>

    Além disso, conforme explicado, podemos aplicar as regras por banco de dados, então, se fosse especificado o método de conexão peer apenas para o banco postgres, não seria possível acessar sem senha em outros bancos.

    Conexão host com IP específico e senha sem criptografia

    Arquivo pg_hba.conf: exemplo de configuração de conexão
    host		all		all		192.168.1.10/32		password

    Como você pode notar, foi solicitada a senha para acessar o banco de outro IP:

    Debian shell: acessando o psql com senha
    fabio@debian-2:~$ psql -h 64.23.248.23 -p 5432 -U fabio -d postgres
    Password for user fabio:
    psql (16.4 (Debian 16.4-1.pgdg120+1), server 16.2 (Debian 16.2-1.pgdg120+2))
    SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
    Type "help" for help.
    
    postgres=>

    Mas, se a tentativa de acessar fosse feita da rede local com o mesmo usuário fabio, considerando que temos no mesmo arquivo a linha de regras exemplificadas anteriormente, este seria o resultado:

    Debian shell: acessando o psql sem inserção de senha (com peer)_
    fabio@debian:~$ psql -U fabio -d postgres
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    Type "help" for help.
    
    postgres=>

    Conexão host para grupo de usuários com IP específico e trust

    Arquivo pg_hba.conf: exemplo de configuração de conexão
    host	    all	 +groupname	      192.168.1.20/32	trust

    Esse cenário permite que os usuários pertencentes ao grupo editors se conectem de um IP específico sem a necessidade de informar senha. No nosso caso, o usuário ana está neste grupo:

    Debian shell: acessando o psql sem inserção de senha
    ana@debian-2:~$ psql -h 64.23.248.23 -p 5432 -U ana -d postgres
    psql (16.4 (Debian 16.4-1.pgdg120+1), server 16.2 (Debian 16.2-1.pgdg120+2))
    SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
    Type "help" for help.
    
    postgres=>

    Conexão host para bancos de dados com o mesmo proprietário (samerole)

    Arquivo pg_hba.conf: exemplo de configuração de conexão
    host    samerole    all     192.168.1.0/24    md5

    Com essa configuração, se o usuário user1 tentar acessar o banco de dados que ele mesmo possui, a senha será solicitada, porém, se ele tentar acessar um banco que pertence a outro usuário, como user2, ele não conseguirá, pois a regra só se aplica aos bancos que possuem o mesmo proprietário.

    Considerações importantes

    • As regras são aplicadas na ordem em que aparecem no arquivo;
    • As primeiras regras têm prioridade, então coloque as mais restritivas no início;
    • Após modificar o pg_hba.conf, recarregue as configurações com o comando systemctl reload postgresql;
    • Se não funcionar, deve-se checar os logs do banco ou então consultar a tabela pg_hba_file_rule do catálogo para entender onde está o erro dentro do pg_hba.conf ;
    Debian shell: logs do banco de dados exibindo erro no pg_hba.conf
    $ tail postgresql-16-main.log
    2024-11-13 17:29:31.029 UTC [141035] LOG:  received SIGHUP, reloading configuration files
    2024-11-13 17:29:31.037 UTC [141035] LOG:  invalid connection type "hoost"
    2024-11-13 17:29:31.037 UTC [141035] CONTEXT:  line 126 of configuration file "/etc/postgresql/16/main/pg_hba.conf"
    2024-11-13 17:29:31.037 UTC [141035] LOG:  /etc/postgresql/16/main/pg_hba.conf was not reloaded
    Debian shell: consulta na tabela pg_hba_file_rules com registro de erro
    postgres=# select * from pg_hba_file_rules ;
     rule_number |              file_name              | line_number | type  |   database    | user_name  |  address  |                 netmask                 |  auth_method  | options |              error
    -------------+-------------------------------------+-------------+-------+---------------+------------+-----------+-----------------------------------------+---------------+---------+---------------------------------
               1 | /etc/postgresql/16/main/pg_hba.conf |         118 | local | {all}         | {postgres} |           |                                         | peer          |         |
               2 | /etc/postgresql/16/main/pg_hba.conf |         125 | local | {all}         | {all}      |           |                                         | trust         |         |
                 | /etc/postgresql/16/main/pg_hba.conf |         126 |       |               |            |           |                                         |               |         | invalid connection type "hoost"
               3 | /etc/postgresql/16/main/pg_hba.conf |         128 | host  | {all}         | {all}      | ::1       | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | scram-sha-256 |         |
               4 | /etc/postgresql/16/main/pg_hba.conf |         131 | local | {replication} | {all}      |           |                                         | peer          |         |
               5 | /etc/postgresql/16/main/pg_hba.conf |         132 | host  | {replication} | {all}      | 127.0.0.1 | 255.255.255.255                         | scram-sha-256 |         |
               6 | /etc/postgresql/16/main/pg_hba.conf |         133 | host  | {replication} | {all}      | ::1       | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | scram-sha-256 |         |
    (7 rows)
    • Teste as configurações em um ambiente de desenvolvimento antes de aplicá-las em produção.

    Ident file

    O ident file é um arquivo utilizado pelo PostgreSQL para mapear nomes de usuários do sistema operacional para nomes de usuários do banco de dados. Ele é útil quando você usa o método de autenticação ident, que verifica a identidade do usuário que está tentando se conectar ao banco de dados com base no nome de usuário do sistema operacional.

    Para fazer essa autenticação baseada nos usuários do SO, o PostgreSQL consulta o ident file para mapear o nome de usuário do SO para o nome de usuário do banco de dados.

    Esse arquivo geralmente é chamado pg_ident.conf e está localizado no mesmo diretório que o arquivo pg_hba.conf (/etc/postgresql/<versão>/<cluster>/pg_hba.conf para sistemas de base Linux). 

    Seu conteúdo é simples, com cada linha contendo três colunas:

    1. Mapa de identidade: nome de um grupo de mapeamento definido;
    2. Nome de usuário do SO: nome do usuário no sistema operacional;
    3. Nome de usuário do banco: nome do usuário no banco de dados que corresponde ao usuário do SO. 

    Após feita a configuração do arquivo pg_ident.conf, para utilizá-lo basta selecionar como método de autenticação no pg_hba.conf o ident e, então, como última opção da linha de regra, informar o nome do mapa definido no ident file. Exemplo:

    Arquivo pg_ident.conf: exemplo de conteúdo
    mymap   seu_usuario_os   seu_usuario_db

    Nesse caso, mymap é o nome do seu mapeamento, seu_usuario_os é o nome do usuário no sistema operacional, e seu_usuario_db é o nome do usuário no banco de dados.

    Para aplicar isso no pg_hba.conf, poderíamos incluir essa linha nas regras do arquivo:

    Arquivo pg_hba.con:f: exemplo de configuração com ident file
    host   all	all   192.168.1.10/32   ident   mymap

    Sendo que ident é o método escolhido, e a opção extra mymap refere-se ao mapeamento que queremos utilizar (considerando que podemos mapear vários usuários no ident file).

    Conclusão

    O pg_hba.conf é uma peça fundamental na definição de políticas de acesso ao banco de dados. Ele é relativamente simples de configurar e tem uma sintaxe bastante intuitiva se compararmos com outros bancos de dados por aí. No entanto, conhecer as opções disponíveis é uma condição indispensável para administrar um ambiente de produção de forma minimamente segura. 

    Por padrão, as instalações do PostgreSQL vêm apenas habilitadas para permitir conexões locais, o que gera uma certa frustração em novatos. Algumas pessoas, na pressa de colocar o banco de dados no ar, acabam utilizando o método TRUST ou colocando faixas de IPs abertas para o mundo todo com 0.0.0.0/0, o que deixa o servidor completamente vulnerável a ataques. Na mesma linha, você deve utilizar o ALL para usuários, e nomes de bancos de dados devem ser utilizados com moderação. Aqui vale a regra do “menor privilégio possível”. Você deve sempre colocar as regras mais restritas possíveis para permitir que as aplicações e os usuários acessem o banco de dados com segurança. 

    Outra tendência forte é utilizar conexões com criptografia SSL para todas as conexões que não forem locais, evitando possíveis ataques de pessoas que ficam escutando o tráfego da sua rede. Isso é tão importante hoje que escrevemos um artigo só sobre isso, que você poderá conferir mais adiante. Portanto, seja cuidadoso e crie regras com prudência. Lembre-se, você pode alterá-las e adicionar quantas linhas forem necessárias depois; e recarregar o arquivo pg_hba.conf sem precisar reiniciar o serviço do PostgreSQL, apenas dando um RELOAD.

  • Catálogo do sistema (pg_catalog)

    Catálogo do sistema (pg_catalog)

    Introdução

    O pg_catalog é um schema especial no PostgreSQL que guarda todos os segredos do seu banco de dados. Quer saber como suas tabelas são organizadas? Lá podemos encontrar essa e outras respostas.

    Neste artigo, vamos dar uma olhada nesse recurso, descobrindo como ele funciona, o que você pode encontrar nele e como pode ser útil para você.

    Roteiro:

    • O que é o pg_catalog?
    • Principais tabelas e views do cátalogo;
    • Exemplos de junções de tabelas do catálogo;
    • Information schema;
    • Objetos físicos e lógicos;
    • Objetos globais e locais.

    O que é?

    Como dito anteriormente, o pg_catalog é um schema interno e padrão do PostgreSQL que guarda o catálogo do sistema, que nada mais é do que um conjunto de tabelas que contém metadados sobre o banco de dados. Essas tabelas registram informações cruciais, como a estrutura das tabelas, índices, tipos de dados, usuários e permissões.

    Todos os bancos de dados do PostgreSQL possuem essas tabelas, pois, no momento da criação de um novo banco, elas são copiadas de um template (modelo), que vem dos bancos padrão template0 e template1 (existem dois desses, pois em um é possível fazer modificações, e no outro, não, o que garante uma cópia de segurança do template).

    Abaixo seguem visualizações de consultas dos bancos de dados existentes, em que podemos ver o template0 e template1. Primeiro, vamos consultar de uma forma já ensinada no artigo de psql, com o comando \l (as três últimas colunas da saída foram ocultadas para melhor visualização):

    psql shell: consultando os bancos de dados pelo comando \l
    postgres@postgres# \l
                                       List of databases
         Name      |    Owner     | Encoding | Locale Provider | Collate |  Ctype  |   
    ---------------+--------------+----------+-----------------+---------+---------+
     aplicacao     | app_admin    | UTF8     | libc            | C.UTF-8 | C.UTF-8 |             
     pagila        | admin_pagila | UTF8     | libc            | C.UTF-8 | C.UTF-8 |
     postgres      | postgres     | UTF8     | libc            | C.UTF-8 | C.UTF-8 |
     template0     | postgres     | UTF8     | libc            | C.UTF-8 | C.UTF-8 |
     template1     | postgres     | UTF8     | libc            | C.UTF-8 | C.UTF-8 | 
    (5 rows)

    Agora, vamos consultar pela tabela pg_database, a qual compõe o catálogo do sistema:

    psql shell: consultando os bancos de dados pela tabela pg_database
    [local]: postgres@postgres=# SELECT datname FROM pg_database;
      datname
    -----------
     postgres
     template1
     template0
     pagila
     aplicacao
    (5 rows)

    Apesar de, na prática, parecerem comandos diferentes, por baixo dos panos ambos fazem a mesma coisa, que é a consulta da tabela pg_database. Para observar isso, podemos ativar a variável ECHO_HIDDEN, que mostra as consultas realizadas nos comandos do psql. Para ativar, basta rodar:

    psql shell: ativando a variável ECHO_HIDDEN
    [local]: postgres@postgres=# \set ECHO_HIDDEN on

    E, então, executar o \l novamente e, assim, vermos o que é feito de forma oculta para nos trazer a listagem de banco de dados:

    psql shell: consultando os bancos de dados com o comando \l e visualizando a query executada
    [local]: postgres@postgres=# \l
    ********* QUERY **********
    SELECT
      d.datname as "Name",
      pg_catalog.pg_get_userbyid(d.datdba) as "Owner",
      pg_catalog.pg_encoding_to_char(d.encoding) as "Encoding",
      CASE d.datlocprovider WHEN 'c' THEN 'libc' WHEN 'i' THEN 'icu' END AS "Locale Provider",
      d.datcollate as "Collate",
      d.datctype as "Ctype",
      d.daticulocale as "ICU Locale",
      d.daticurules as "ICU Rules",
      pg_catalog.array_to_string(d.datacl, E'\n') AS "Access privileges"
    FROM pg_catalog.pg_database d
    ORDER BY 1;
    **************************
                                                          List of databases
       Name    |    Owner     | Encoding | Locale Provider | Collate |  Ctype  | 
     ---------+--------------+----------+-----------------+---------+----------+
     aplicacao | app_admin    | UTF8     | libc            | C.UTF-8 | C.UTF-8 |
     pagila    | admin_pagila | UTF8     | libc            | C.UTF-8 | C.UTF-8 |
     postgres  | postgres     | UTF8     | libc            | C.UTF-8 | C.UTF-8 | 
     template0 | postgres     | UTF8     | libc            | C.UTF-8 | C.UTF-8 |
     template1 | postgres     | UTF8     | libc            | C.UTF-8 | C.UTF-8 |
    (5 rows)

    A variável ECHO_HIDDEN também pode ser usada dessa forma para os diversos outros comandos do psql, possibilitando a visibilidade da importância dos dados presentes nas tabelas do catálogo do sistema. Recomendamos que teste sem moderação!

    Principais tabelas e views do catálogo

    pg_authid

    Essa tabela armazena informações sobre os usuários de autenticação (no PostgreSQL utilizamos o termo “roles” em inglês tanto para usuários como para grupos de usuários). Ela contém detalhes como nome de usuário, tipo de papel (usuário ou grupo), senha criptografada (se aplicável) e outras informações relacionadas à autenticação e a permissões de acesso. Você pode acessar a documentação dessa tabela por aqui. Entre as principais colunas, temos:

    • rolname: nome do usuário ou grupo de usuários;
    • rolsuper: indica se o usuário tem privilégios de superusuário;
    • rolcreaterole: indica se o usuário tem o privilégio para criar outros usuários ou grupos;
    • rolcreatedb: indica se o usuário pode criar bancos de dados;
    • rolcanlogin: mostra se o usuário pode efetuar login como um usuário ou se apenas se comporta como um grupo de usuários;
    • rolreplication: indica se o usuário pode ser usado para replicação de dados.

    pg_roles

    Essa view do sistema também usa dados da tabela pg_authid para fornecer informações sobre todas as roles (usuários e grupo de usuários) do banco de dados, a diferença é a ocultação completa da senha (na outra vemos ela criptografada). A documentação oficial pode ser acessada aqui.

    pg_database

    Essa tabela contém uma linha para cada banco de dados no sistema PostgreSQL. Ela armazena várias informações importantes sobre cada banco, incluindo seu nome, dono e configurações específicas. Sua documentação oficial pode ser encontrada aqui. As principais colunas são:

    • datname: nome do banco de dados;
    • datdba: OID (Object Identifier ou Identificador de Objeto) do dono do banco de dados (esse OID fica na tabela pg_authid, que vimos anteriormente);
    • encoding: codificação de caracteres do banco de dados;
    • datctype: tipo de caracteres padrão do banco de dados;
    • datistemplate: indica se o banco de dados pode ser usado como um template (modelo), conforme os bancos template0 e template1;
    • datallowconn: indica se conexões ao banco de dados são permitidas, uma vez que, por padrão, não são permitidas conexões no template0;
    • datconnlimit: limite máximo de conexões ao banco de dados;
    • dattablespace: OID da tablespace em que o banco está;
    • datacl: lista de controle de acesso do banco de dados.

    pg_namespace 

    Essa tabela armazena informações sobre os schemas no banco de dados. Os schemas são usados para organizar objetos como tabelas, índices, funções etc. no PostgreSQL, fornecendo uma estrutura lógica de organização e controle de permissões como se fosse pastas no banco de dados. A documentação oficial pode ser conferida aqui. As colunas dessa tabela são:

    • oid: identificador do schema;
    • nspname: nome do schema;
    • nspowner: OID do usuário dono do schema (faz ponte com a tabela pg_authid);
    • nspacl: lista o controle de acesso do schema.

    pg_class

    Nessa tabela, temos diversas informações sobre tabelas e outros objetos similares (como views, índices etc.). É possível ver uma descrição de cada coluna dessa tabela aqui na documentação oficial. Algumas das colunas que destacamos são:  

    • oid: número identificador do objeto;
    • relname: nome do objeto;
    • relnamespace: OID (identificador) do schema ao qual esse objeto pertence (referência pg_namespace);
    • relowner: OID do dono do objeto (referência pg_authid);
    • relfilenode: número identificador do arquivo físico que armazena o objeto em questão (que geralmente é igual ao OID, citado no início da lista. Citaremos adiante um caso em que eles não são iguais);
    • reltablespace: OID do tablespace onde o objeto está armazenado. Se for 0, o objeto está no tablespace padrão do banco de dados (referência pg_tablespace);
    • relpages: número de páginas de disco usado pelo objeto (cada página tem por padrão 8 kb, e se o objeto for maior do que isso, ocupará múltiplos desse valor em número de páginas. Por exemplo, um objeto de 36 kb ocupará 5 páginas, não 4,5);
    • reltuples: número de linhas na tabela;
    • relkind: uma letra que indica o tipo do objeto:
      • r = tabela, 
      • i = índice 
      • s = sequência etc.
    • relacl: lista de controle de acesso do objeto, ou seja, mostra os usuários e suas permissões de acesso ao objeto;
    • reloptions: opções de ajustes para otimização de objetos.

    pg_attribute

    Armazena informações sobre cada atributo, ou seja, a coluna, de todas as tabelas do banco de dados, incluindo nome das colunas, tipo de dados, restrições etc. A documentação oficial está aqui. Suas principais colunas são:

    • attrelid: o OID da tabela ao qual essa coluna pertence (referência pg_class);
    • attname: o nome da coluna;
    • attypid: o OID do tipo de dado dessa coluna (referência pg_type);
    • attnotnull: indica se a coluna foi definida como ‘NOT NULL’;
    • atthasdef: indica se a coluna tem valor padrão definido.

    pg_type

    Nessa tabela, encontramos informações sobre os tipos de dados do banco, que podem ser de tabelas, colunas etc. Sua documentação está nessa página. As principais colunas são:

    • typname: nome do tipo de dado;
    • typnamespace: OID do schema que contém o tipo de dado em questão (referência pg_namespace);
    • typlen: tamanho em bytes do tipo de dado;
    • typtype: indica o tipo de dado, ou seja, muda conforme o objeto ao qual está se referenciando, sendo b para tipo base, c para tipo composto (o caso das tabelas), d para domínio etc.

    pg_constraints

    Essa tabela armazena informações sobre as restrições (constraints) definidas em diferentes objetos do banco de dados, como tabelas. As restrições são regras que impõem integridade de dados e outras condições para garantir que os dados no banco de dados estejam consistentes e de acordo com as regras de negócio. A documentação oficial está aqui. Principais colunas:

    • conname: nome da constraint;
    • connamespace: OID do esquema ao qual a constraint pertence (referente a pg_namespace);
    • contype: tipo da restrição, como PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK etc.;
    • conrelid: OID da tabela à qual a restrição está associada (referente a pg_class).

    pg_proc

    Essa tabela armazena informações sobre as funções e os procedimentos (functions e procedures em inglês) definidos no banco de dados. Sua documentação está aqui. Algumas das principais colunas são:

    • proname: nome da função;
    • prolang: linguagem na qual a função é escrita (referente a pg_language);
    • prorettype: tipo de retorno da função (referente a pg_type);
    • proargtypes: tipos dos argumentos da função (referente a pg_type).

    pg_sequence

    Armazena informações sobre as sequências definidas no banco de dados. Sequências são objetos especiais usados para gerar números únicos e sequenciais, frequentemente utilizados para colunas de chave primária artificiais. A documentação pode ser achada aqui. Suas colunas são:

    • seqrelid: OID (identificador) do objeto da sequência em questão (referente a pg_class);
    • seqtypid: tipo de dados da sequência, por exemplo, integer, bigint etc. (referente a pg_type);
    • seqstart: valor inicial da sequência;
    • seqincrement: incremento da sequência;
    • seqcache: número de valores da sequência a serem cacheados (colocados em cache);
    • seqcycle: indica se a sequência cicla ao atingir o valor máximo/mínimo (t para TRUE/f para FALSE).

    pg_stats

    Essa view fornece um acesso mais amigável às estatísticas armazenadas na tabela pg_statistic, que conta com estatísticas sobre o conteúdo de cada coluna de cada tabela do banco. Essas estatísticas são usadas pelo otimizador de consultas para gerar planos de execução eficientes. A documentação oficial está nesta página, já a página da pg_statistic você encontra aqui. As principais colunas da pg_stats são:

    • schemaname: nome do esquema da tabela;
    • tablename: nome da tabela;
    • attname: nome da coluna;
    • avg_width: largura média dos valores da coluna (em bytes);
    • most_common_vals: valores mais comuns na coluna;
    • most_common_freqs: frequências dos valores mais comuns.

    pg_settings

    Essa view nos permite visualizar parâmetros de tempo de execução do servidor, ou seja, nos permite visualizar e modificar parâmetros de configuração em tempo real. A documentação pode ser encontrada aqui. Além disso, temos um artigo antigo sobre configurações do PostgreSQL citando essa tabela, podendo ser interessante para ver exemplos.
    Suas principais colunas são:

    • name: nome do parâmetro;
    • setting: valor atual do parâmetro;
    • category: grupo lógico do parâmetro;
    • short_desc: descrição resumida do parâmetro.

    Exemplos de junção (JOIN) de tabelas do catálogo

    Tabelas pg_database e pg_authid

    Abaixo temos um exemplo que nos mostra os seguintes dados:

    • Tabela pg_database:
      • OID e nome do banco;
      • OID do usuário dono do banco.
    • Tabela pg_authid:
      • Nome do usuário em questão.
    psql shell: consultando dados das tabelas pg_database e pg_authid
    [local]: postgres@postgres=# SELECT 
    d.oid, 
    d.datname, 
    d.datdba, 
    a.rolname 
    FROM pg_database d 
    JOIN pg_authid a ON d.datdba = a.oid;
    
      oid  |      datname      | datdba |   rolname
    -------+-------------------+--------+--------------
     18206 | readme_to_recover |     10 | postgres
     18203 | banco_tblspc      |     10 | postgres
         4 | template0         |     10 | postgres
         1 | template1         |     10 | postgres
         5 | postgres          |     10 | postgres
     16539 | aplicacao         |  16520 | app_admin
     17660 | pagila            |  17189 | admin_pagila
    (7 rows)

    Tabelas pg_class, pg_attribute e pg_type

    Agora, vamos ver uma junção com os dados de tabelas, colunas da tabela e tipos de dados:

    • Tabela pg_class:
      • OID e nome do objeto;
      • OID do tipo de dado na tabela pg_type.
    • Tabela pg_attribute:
      • Número da coluna (posição dela em relação à tabela);
      • Nome da coluna.
    • Tabela pg_type:
      • Nome do tipo de dado;
      • Tipo de dado do dado, que varia conforme o objeto em questão. Aqui vamos utilizar a cláusula CASE WHEN para nomear os objetos tipo base explicitamente.
    psql shell: consultando dados das tabelas pg_class, pg_attribute e pg_type
    SELECT
        c.oid AS oid_tab,
        c.relname AS nome_tab,
        a.attnum AS num_col,
        a.attname AS nome_col,
        t.oid AS oid_tipo,
        t.typname AS nome_tipo,
        CASE
            WHEN t.typtype = 'b' THEN 'tipo base'
            END AS tipo
    FROM pg_class c
    JOIN pg_attribute a ON c.oid = a.attrelid
    JOIN pg_type t ON a.atttypid = t.oid
    WHERE 
    c.relname = 'pg_class' 
    AND a.attnum > 0
    ORDER BY 
    c.relname, 
    a.attnum;
    
        oid_tab |   nome_tab  | num_col  |   nome_col  | oid_tipo |nome_tipo |   tipo
    ------------+-------------+------------+---------------------+----------+------------+
           1259 | pg_class    |        1 | oid         |       26 | oid      | tipo base
           1259 | pg_class    |        2 | relname     |       19 | name     | tipo base
           1259 | pg_class    |        3 |relnamespace |       26 | oid      | tipo base
           1259 | pg_class    |        4 | reltype     |       26 | oid      | tipo base
           1259 | pg_class    |        5 | reloftype   |       26 | oid      | tipo base
           1259 | pg_class    |        6 | relowner    |       26 | oid      | tipo base
           1259 | pg_class    |        7 | relam       |       26 | oid      | tipo base
           1259 | pg_class    |        8 | relfilenode |       26 | oid      | tipo base
           1259 | pg_class    |        9 |reltablespace|       26 | oid      | tipo base
           ...

    information_schema

    O information_schema é um schema padronizado definido pelo ISO que fornece uma maneira portável e independente do fornecedor do banco de dados para se obter informações sobre os objetos do banco de dados, assim como o pg_catalog, rodando, na verdade, em cima dele, utilizando-o como fonte de dados. Esse schema é também suportado por muitos outros sistemas de gerenciamento de banco de dados (SGBDs). A documentação oficial do PostgreSQL pode ser encontrada aqui.

    Assim como através das tabelas do catálogo, aqui podemos listar todas as tabelas de um determinado schema utilizando uma tabela do information_schema:

    psql shell: consultando dados do information_schema
    localhost: dba@aplicacao=# SELECT 
    table_schema, 
    table_name 
    FROM information_schema.tables 
    ORDER BY table_schema;
    
        table_schema    |              table_name
    --------------------+---------------------------------------
     information_schema | collations
     information_schema | information_schema_catalog_name
     information_schema | applicable_roles
     information_schema | domain_constraints
     information_schema | administrable_role_authorizations
     information_schema | collation_character_set_applicability
     information_schema | attributes
     information_schema | character_sets
     information_schema | column_udt_usage
     information_schema | check_constraint_routine_usage
     information_schema | column_column_usage
    ...

    As principais diferenças entre o information_schema e o pg_catalog são:

    • Padrão: conforme citado, o information_schema segue o padrão ISO SQL e é compatível com diversos outros servidores, já o pg_catalog é específico do PostgreSQL;
    • Nomenclaturas: como o information_schema segue o padrão ISO SQL, é notável que seus objetos têm nomenclaturas mais simples e explícitas, diferente do pg_catalog, em que há termos mais técnicos e específicos do Postgres;
    • Nível de detalhe: o information_schema fornece uma visão mais simplificada dos metadados do banco, enquanto o pg_catalog traz informações mais específicas e de baixo nível sobre os objetos do banco de dados;
    • Informações sensíveis: no pg_catalog é possível encontrar informações sensíveis, por isso é um schema mais utilizado pelos DBAs, ao passo que o information_schema foi projetado para acesso público, ocultando informações sensíveis como senhas.

    A seguir, está uma maneira de listar todas as tabelas do information_schema (informação que pode ser encontrada na documentação disponibilizada anteriormente) utilizando o pg_catalog:

    psql shell: consultando informações do information_schema por tabelas do catálogo (pg_class e pg_namespace)
    localhost: dba@aplicacao=# SELECT 
    c.relname AS table_name 
    FROM pg_class c 
    JOIN pg_namespace n ON n.oid = c.relnamespace 
    WHERE 
    n.nspname = 'information_schema' 
    AND c.relkind = 'v' 
    ORDER BY c.relname;
    
                  table_name
    ---------------------------------------
     administrable_role_authorizations
     applicable_roles
     attributes
     character_sets
     check_constraint_routine_usage
     check_constraints
     collation_character_set_applicability
     collations
     column_column_usage
     column_domain_usage
     column_options
     column_privileges
     column_udt_usage
     columns
     constraint_column_usage
     constraint_table_usage
     data_type_privileges
    ...

    Objetos físicos e lógicos

    Objetos físicos referem-se aos componentes do banco de dados que têm representação física no sistema de arquivos do PostgreSQL, ou seja, além de estarem presentes no banco, também são representados por arquivos em disco. Alguns dos principais objetos físicos são:

    • Tabelas (incluindo, portanto, as do catálogo);
    • Ínidices;
    • Tablespaces;
    • Databases etc.

    Podemos encontrar esses objetos nas pastas nomeadas base e global, a depender de serem globais ou locais. Mas não se preocupe, no próximo tópico do artigo, explicaremos mais sobre objetos globais e locais.
    Abaixo, seguem exemplos de arquivos de objetos, que são nomeados com números, presentes no diretório global no PGDATA

    Debain shell: listando arquivos e diretórios do diretório global
    $ ls -lha $PGDATA/global
    total 644K
    drwx------  2 postgres postgres 4.0K May 23 17:56 .
    drwx------ 19 postgres postgres 4.0K Apr  9 18:41 ..
    ...
    -rw-------  1 postgres postgres 8.0K May 16 14:44 1260
    -rw-------  1 postgres postgres  24K Mar 27 15:39 1260_fsm
    -rw-------  1 postgres postgres 8.0K Mar 27 15:49 1260_vm
    -rw-------  1 postgres postgres 8.0K May 13 19:14 1261
    -rw-------  1 postgres postgres  24K Mar 27 15:39 1261_fsm
    -rw-------  1 postgres postgres 8.0K Apr 22 15:11 1261_vm
    -rw-------  1 postgres postgres 8.0K Jun  4 19:45 1262
    ...
    -rw-------  1 postgres postgres 8.0K Jun  4 19:45 pg_control
    -rw-------  1 postgres postgres  524 Mar 27 15:39 pg_filenode.map
    -rw-------  1 postgres postgres  29K May 23 17:56 pg_internal.init

    A seguir, estão os arquivos dos objetos que se encontram no diretório base, também no PGDATA:

    Debian shell: listando arquivos e diretórios do diretório base
    # ls -lha $PGDATA/base
    total 76K
    drwx------ 11 postgres postgres 4.0K Jun  4 19:40 .
    drwx------ 19 postgres postgres 4.0K Apr  9 18:41 ..
    drwx------  2 postgres postgres 4.0K May 13 18:50 1
    drwx------  2 postgres postgres 4.0K Apr  9 19:11 16398
    drwx------  2 postgres postgres  12K Apr 15 18:50 16449
    drwx------  2 postgres postgres  12K May 23 17:55 16539
    drwx------  2 postgres postgres 4.0K May  8 13:46 16645
    drwx------  2 postgres postgres  12K May 16 12:37 17660
    drwx------  2 postgres postgres 4.0K Mar 27 15:39 4
    drwx------  2 postgres postgres  12K May 23 18:05 5
    drwx------  2 postgres postgres 4.0K May 23 17:55 pgsql_tmp

    Já os objetos lógicos são abstrações que não têm uma representação física direta no sistema de arquivos, mas são gerenciadas pelo PostgreSQL. Alguns desses objetos são schemas, views, funções etc.

    Objetos globais e locais

    Objetos globais são aqueles que não pertencem a um banco de dados específico e podem ser acessados por qualquer banco de dados de uma instância no PostgreSQL. É possível encontrá-los no diretório global no PGDATA, que vimos antes: 

    Debian shell: listando arquivos e diretórios do diretório global, com ênfase no arquivo 1262
    $ ls -lha $PGDATA/global
    total 644K
    drwx------  2 postgres postgres 4.0K May 23 17:56 .
    drwx------ 19 postgres postgres 4.0K Apr  9 18:41 ..
    ...
    -rw-------  1 postgres postgres 8.0K May 16 14:44 1260
    -rw-------  1 postgres postgres  24K Mar 27 15:39 1260_fsm
    -rw-------  1 postgres postgres 8.0K Mar 27 15:49 1260_vm
    -rw-------  1 postgres postgres 8.0K May 13 19:14 1261
    -rw-------  1 postgres postgres  24K Mar 27 15:39 1261_fsm
    -rw-------  1 postgres postgres 8.0K Apr 22 15:11 1261_vm
    -rw-------  1 postgres postgres 8.0K Jun  4 19:45 1262
    ...
    -rw-------  1 postgres postgres 8.0K Jun  4 19:45 pg_control
    -rw-------  1 postgres postgres  524 Mar 27 15:39 pg_filenode.map
    -rw-------  1 postgres postgres  29K May 23 17:56 pg_internal.init

    É importante notar que os nomes dos arquivos não são números aleatórios: geralmente eles são o mesmo número OID do objeto em questão. Por exemplo, nesta saída temos o arquivo 1262 e, através de uma consulta a uma tabela do catálogo do sistema no psql, a pg_class (nela é possível encontrar informações de alguns objetos do banco de dados, como tabelas, índices, views etc.), e buscando pelo OID 1262, é possível descobrir que esse arquivo representa a tabela pg_database, conforme saída da consulta a seguir:

    psql shell: consultando informações do objeto global 1262
    postgres@postgres=# SELECT 
    oid,
    relname 
    FROM pg_class 
    WHERE oid = 1262;
    
     oid  |   relname
    ------+-------------
     1262 | pg_database
    (1 row)

    Nessa consulta, utilizamos as colunas relisshared e relkind como filtro para encontrar as tabelas do catálogo na tabela pg_class. Isso porque a coluna relisshared é de valor booleano, sendo verdadeira para indicar uma tabela global (do catálogo do sistema), e a coluna relkind traz o tipo de objeto, sendo ‘r’ representativo de tabelas. A seguir podemos ver outras tabelas globais do catálogo pela pg_class:

    psql shell: consultando as tabelas do catálogo utilizando a tabela pg_class
    postgres@postgres=# SELECT 
    oid,
    relname 
    FROM pg_class 
    WHERE 
    relisshared 
    AND relkind = 'r';
    
     oid  |        relname
    ------+-----------------------
     1260 | pg_authid
     6100 | pg_subscription
     1262 | pg_database
     2964 | pg_db_role_setting
     1213 | pg_tablespace
     1261 | pg_auth_members
     1214 | pg_shdepend
     2396 | pg_shdescription
     6000 | pg_replication_origin
     3592 | pg_shseclabel
     6243 | pg_parameter_acl
    (11 rows)

    Outro exemplo de objeto global são as tablespaces, que são locais de armazenamento que contêm dados de bancos PostgreSQL fora do diretório base, o PGDATA. Aqui criamos uma chamada novaTablespace e um banco de dados dentro dela, chamado banco_tblspc.

    Além do local onde a tablespace foi criada, também podemos encontrá-la via link simbólico no caminho $PGDATA/pg_tblspc/

    Debian shell: listando a tablespace criada
    # ls -l $PGDATA/pg_tblspc/
    total 0
    lrwxrwxrwx 1 postgres postgres 26 Jun 11 17:22 18202 -> /var/lib/postgresql/tblspc

    Dentro dela, temos mais um diretório, que abriga o nosso banco recém-criado:

    Debian shell: listandso os diretórios da tablespace criada
    # ls -lha $PGDATA/pg_tblspc/18202
    drwx------ 3 postgres postgres 4.0K Jun 11 17:22 .
    drwxr-xr-x 6 postgres postgres 4.0K Jun 11 17:34 ..
    drwx------ 3 postgres postgres 4.0K Jun 11 17:34 PG_16_202307071

    Então, chegamos no diretório oficial do banco, que vai abrigar os objetos criados nele:

    Debian shell: listando dados do diretório do banco criado
    # ls -lha $PGDATA/pg_tblspc/18202/PG_16_202307071/
    total 12K
    drwx------ 3 postgres postgres 4.0K Jun 11 17:34 .
    drwx------ 3 postgres postgres 4.0K Jun 11 17:22 ..
    drwx------ 2 postgres postgres 4.0K Jun 11 17:34 18203

    O número que nomeia o arquivo do banco também é o OID desse objeto, o que facilita encontrá-lo na pg_database, por exemplo:

    psql shell: consultando dados do objeto que representa o banco criado no catálogo do sistema
    [local]: postgres@postgres=# SELECT * FROM pg_database WHERE oid = 18203;
    -[ RECORD 1 ]--+-------------
    oid            | 18203
    datname        | banco_tblspc
    datdba         | 10
    encoding       | 6
    datlocprovider | c
    datistemplate  | f
    datallowconn   | t
    datconnlimit   | -1
    datfrozenxid   | 722
    datminmxid     | 1
    dattablespace  | 18202
    datcollate     | C.UTF-8
    datctype       | C.UTF-8
    daticulocale   | (null)
    daticurules    | (null)
    datcollversion | (null)
    datacl         | (null)

    E também podemos encontrar nossa tablespace na tabela pg_tablespace:

    psql shell: consultando a tablespace criada no catálogo do sistema
    [local]: postgres@postgres=# select * from pg_tablespace;
      oid  |    spcname     | spcowner | spcacl | spcoptions
    -------+----------------+----------+--------+------------
      1663 | pg_default     |       10 | (null) | (null)
      1664 | pg_global      |       10 | (null) | (null)
     18202 | novatablespace |       10 | (null) | (null)
    (3 rows)

    Já os locais são objetos pertencentes a um banco de dados específico e só podem ser acessados dentro desse banco. Ficam no diretório base, no PGDATA. A seguir, podemos ver a listagem de arquivos desse diretório:

    Debian shell: listando objetos locais no diretório base
    # ls -lha $PGDATA/base
    total 72K
    drwx------ 10 postgres postgres 4.0K Jun  6 03:44 .
    drwx------ 19 postgres postgres 4.0K Apr  9 18:41 ..
    drwx------  2 postgres postgres 4.0K May 13 18:50 1
    drwx------  2 postgres postgres  12K Apr 15 18:50 16449
    drwx------  2 postgres postgres  12K May 23 17:55 16539
    drwx------  2 postgres postgres 4.0K May  8 13:46 16645
    drwx------  2 postgres postgres  12K May 16 12:37 17660
    drwx------  2 postgres postgres 4.0K Mar 27 15:39 4
    drwx------  2 postgres postgres  12K May 23 18:05 5
    drwx------  2 postgres postgres 4.0K May 23 17:55 pgsql_tmp

    Ao entrar nesse diretório, diferente da global em que já vemos os arquivos dos objetos, temos mais um nível de diretórios representando cada banco de dados existente na instância do PostgreSQL, e é dentro de cada um deles que estão armazenados os objetos locais do banco. É possível checar a qual banco de dados cada diretório é referente consultando a tabela pg_database, novamente usando os OIDs de cada objeto para identificá-los:

    psql shell: consultando dados dos bancos de dados
    postgres@postgres=# SELECT oid, datname FROM pg_database;
      oid  |  datname
    -------+-----------
         5 | postgres
         1 | template1
         4 | template0
     17660 | pagila
     16539 | aplicacao
    (5 rows)

    Vamos listar os arquivos do diretório do banco criado há alguns artigos, o aplicacao, diretório 16539:

    Debian shell: listando arquivos do diretório do objeto 16539
    # ls -lha $PGDATA/base/16539/
    total 7.9M
    drwx------ 2 postgres postgres  12K May 23 17:55 .
    drwx------ 8 postgres postgres 4.0K Jun  6 03:56 ..
    -rw------- 1 postgres postgres 8.0K Apr 22 15:41 112
    ...
    -rw------- 1 postgres postgres 8.0K May 10 12:49 16649
    -rw------- 1 postgres postgres 8.0K May  8 17:43 16656
    -rw------- 1 postgres postgres  16K May  8 17:43 16660
    -rw------- 1 postgres postgres 8.0K May 16 12:14 16662
    -rw------- 1 postgres postgres 8.0K May 16 12:14 16663
    -rw------- 1 postgres postgres  16K May 16 12:14 16667
    -rw------- 1 postgres postgres 8.0K May  8 17:43 16690
    -rw------- 1 postgres postgres 8.0K May  9 13:44 16691
    -rw------- 1 postgres postgres  16K May  8 17:43 16695
    -rw------- 1 postgres postgres 8.0K May  8 17:43 16697
    -rw------- 1 postgres postgres 8.0K May  8 17:43 16698
    -rw------- 1 postgres postgres  16K May  8 17:43 16702
    -rw------- 1 postgres postgres 8.0K May  8 17:43 16704
    -rw------- 1 postgres postgres 8.0K May  8 17:43 16705
    -rw------- 1 postgres postgres  16K May  8 17:43 16709
    -rw------- 1 postgres postgres 8.0K Apr 22 15:41 174
    -rw------- 1 postgres postgres 8.0K Apr 22 15:41 175
    -rw------- 1 postgres postgres 8.0K May 16 12:39 18131
    -rw------- 1 postgres postgres 8.0K May 16 12:39 18132
    -rw------- 1 postgres postgres  16K May 16 12:39 18136
    -rw------- 1 postgres postgres 8.0K May 16 12:44 18140
    -rw------- 1 postgres postgres 8.0K May 16 12:44 18141
    -rw------- 1 postgres postgres  16K May 16 12:44 18145
    -rw------- 1 postgres postgres 8.0K May 16 14:39 18148
    -rw------- 1 postgres postgres 8.0K May 16 14:39 18149
    -rw------- 1 postgres postgres  16K May 16 14:39 18153
    ...
    -rw------- 1 postgres postgres    3 Apr 22 15:40 PG_VERSION
    -rw------- 1 postgres postgres  524 Apr 22 15:40 pg_filenode.map
    -rw------- 1 postgres postgres 156K May 23 17:55 pg_internal.init

    A consulta realizada para encontrar o objeto correspondente no catálogo do psql (conectando agora no próprio banco de dados aplicacao) foi:

    psql shell: consultando dados dos objetos do banco aplicacao (16539)
    dba@aplicacao=#  SELECT 
    oid,
    relname 
    FROM pg_class 
    WHERE NOT relisshared AND relkind = 'r' AND relname LIKE '%tabela%';
    
      oid  |   relname
    -------+--------------
     18149 | tabela6_app1
     16691 | tabela1_app2
     16656 | tabela2_app1
     16663 | tabela3_app1
     16698 | tabela2_app2
     16705 | tabela3_app2
     16649 | tabela1_app1
     18132 | tabela4_app1
     18141 | tabela5_app1
    (9 rows)

    Aqui, podemos ver os OIDs de todas as tabelas desse banco, que também são identificáveis como arquivos na nossa consulta no diretório 16539 (banco aplicacao).
    Entre os demais arquivos nesse diretório, temos outras tabelas do catálogo, índices, schemas etc.

    Mas e quando os OIDs de tabelas não são iguais aos números que nomeiam seus arquivos? Um dos casos é quando utilizamos o comando TRUNCATE, que serve para remover todas as linhas de uma tabela e também pode mudar o relfilenode, já que o PostgreSQL pode optar por recriar fisicamente a tabela para otimização da operação.

    Confira uma consulta dos dados da tabela na pg_class antes do TRUNCATE:

    psql shell: consultando dados de uma tabela na pg_class
    localhost: dba@aplicacao=# SELECT 
    oid, 
    relname, 
    relfilenode 
    FROM pg_class 
    WHERE relname = 'tabela1_app1';
    
      oid  |   relname    | relfilenode
    -------+--------------+-------------
     16649 | tabela1_app1 |       16649
    (1 row)

    Execução do TRUNCATE:

    psql shell: executando TRUNCATE na tabela tabela1_app1
    localhost: dba@aplicacao=# TRUNCATE TABLE aplicacao1.tabela1_app1;
    TRUNCATE TABLE

    Nova consulta dos dados da tabela na pg_class, após o TRUNCATE:

    psql shell: consultando dados de uma tabela no pg_class após TRUNCATE
    localhost: dba@aplicacao=# SELECT 
    oid, 
    relname, 
    relfilenode 
    FROM pg_class 
    WHERE relname = 'tabela1_app1';
    
      oid  |   relname    | relfilenode
    -------+--------------+-------------
     16649 | tabela1_app1 |       18204
    (1 row)

    Isso implica termos um novo arquivo para representar essa tabela no diretório do banco de dados, nomeado com a numeração do novo relfilenode:

    Debian shell: listando arquivos do diretório do banco de dados após TRUNCATE (novo arquivo 18204)
    $ ls -lha $PGDATA/base/16539/
    total 7.9M
    drwx------ 2 postgres postgres  12K Jun 11 18:28 .
    drwx------ 8 postgres postgres 4.0K Jun  6 03:56 ..
    ...
    -rw------- 1 postgres postgres    0 Jun 11 18:25 18204
    ...
    -rw------- 1 postgres postgres    3 Apr 22 15:40 PG_VERSION
    -rw------- 1 postgres postgres  524 Apr 22 15:40 pg_filenode.map
    -rw------- 1 postgres postgres 156K Jun 11 18:27 pg_internal.init

    Conclusão

    Exploramos o pg_catalog, um componente crucial do PostgreSQL que armazena metadados essenciais para a administração do banco de dados. Compreender tabelas e views como pg_authid, pg_database e pg_class permite realizar auditorias, otimizações e controle de acessos com eficácia.

    O uso de comandos como \l e a variável ECHO_HIDDEN desmistifica operações internas, proporcionando maior controle e entendimento. O pg_catalog é uma ferramenta poderosa que garante a integridade e a performance do seu sistema de banco de dados, o que é essencial para qualquer DBA ou desenvolvedor.

  • Arquivos, diretórios e processos

    Arquivos, diretórios e processos

    Introdução

    No PostgreSQL, a organização de arquivos e diretórios, bem como a gestão de processos, são componentes fundamentais para entender o seu funcionamento. Este artigo fornecerá uma visão geral de como o PostgreSQL estrutura seus dados e controla seus processos.

    Roteiro:

    • Arquivos e diretórios dos dados no PostgreSQL:
      • Binários
      • Arquivos de configuração
      • Arquivos de logs
      • Subdiretórios
    • Processos

    Arquivos e diretórios dos dados no PostgreSQL

    O PostgreSQL organiza seus dados e configurações em uma estrutura de diretórios específica. A localização desses diretórios pode variar dependendo da instalação, mas geralmente segue um padrão. O diretório principal que contém todos os dados do PostgreSQL é o Base Directory (também conhecido pela variável de ambiente $PGDATA), que é definido durante a inicialização do cluster PostgreSQL com o comando initdb. Para sistemas Linux, o endereço de PGDATA pode variar conforme a distribuição:

    • Debian: /var/lib/postgresql/<versão>/<nome_do_cluster>
    • Red Hat: /var/lib/pgsql/<versão>/data

    Claro, você pode definir qualquer diretório/disco/partição para colocar seus dados, como demonstramos nos artigos anteriores sobre instalação. Portanto é uma boa ideia usar a variável de ambiente $PGDATA (que, a depender da distribuição, já vem setada para o usuário postgres) ou então descobrir esse diretório pelo comando no psql SHOW data_directory:

    psql shell: consultando o diretório de dados
    $ psql -c 'SHOW data_directory;'
           data_directory
    -----------------------------
     /var/lib/postgresql/16/main
    (1 row)

    Nesse diretório, temos arquivos e subdiretórios importantes para o programa, que veremos a seguir.

    Binários

    Dentre os principais arquivos do PostgreSQL, temos os binários, que são executáveis responsáveis por iniciar e interagir com o banco de dados. Eles estão em diferentes diretórios, que mudam conforme o sistema operacional. Considerando essas três distribuições Linux, podemos encontrar os principais binários em:

    • Debian:
      • /usr/share/postgresql-common: contém arquivos e scripts comuns utilizados por várias versões do PostgreSQL, por exemplo, pg_ctlcluster e pg_createcluster;
      • /usr/lib/postgresql/<sua_versão>/bin: neste diretório, você pode encontrar os binários específicos da versão do PostgreSQL, como o próprio psql, os utilitários createdb, initdb etc.
    • Red Hat:
      • /usr/pgsql/<versão>/bin/: guarda binários e executáveis específicos de cada versão do PostgreSQL.
    • Instalação via compilação:
      • /usr/local/pgsql/bin/

    Arquivos de configuração

    • postgresql.conf: principal arquivo de configuração do servidor PostgreSQL. Contém parâmetros para ajustar o desempenho, a segurança, o comportamento e outras opções do servidor. Em distribuições Debian e derivados, este arquivo está em outro local, num diretório que agrega todos os arquivos de configuração: /etc/postgresql/<versão>/<nome_do_cluster>;
    • pg_hba.conf: configurações de autenticação do host. Define quem pode conectar-se ao banco de dados e como as conexões são autenticadas. Assim como antes, em distribuições Debian e derivados, este arquivo fica em outro local;
    • pg_ident.conf: mapeamentos para autenticação do tipo ident no pg_hba.conf. Usado para mapear nomes de usuários do sistema operacional para nomes de usuários do PostgreSQL. Mais uma vez, em distribuições Debian e derivados, este arquivo fica em outro local, vide acima;
    • postgresql.auto.conf: armazena as configurações definidas dinamicamente através do comando ALTER SYSTEM SET, bem como outras configurações definidas automaticamente pelo próprio PostgreSQL. Você não deve editar este arquivo manualmente!;
    • PG_VERSION: contém a versão do PostgreSQL em uso no cluster (major version);
    • postmaster.pid: contém o ID do processo (PID) do servidor PostgreSQL em execução e outras informações, como diretório de dados do cluster, data de início do processo etc.;
    • postmaster.opts: arquivo que registra as opções de linha de comando usadas por último na inicialização do servidor PostgreSQL.

    Arquivos de logs

    Dentro do $PGDATA, temos outros diretórios importantes que guardam arquivos do PostgreSQL, como os arquivos de log. Normalmente, os logs ficam em $PGDATA/logs, mas em distribuições Debian e derivadas eles estão localizados em /var/log/postgresql, como podemos ver na saída abaixo. 

    Debian shell: logs do PostgreSQL
    # ls -lh /var/log/postgresql/*
    total 6.6M
    drwxrwxr-t  2 root     postgres 4.0K May 19 00:00 .
    drwxr-xr-x 10 root     root     4.0K May  1 00:00 ..
    -rw-r-----  1 postgres adm      123K May 23 09:40 postgresql-16-main.log
    -rw-r-----  1 postgres adm      6.0M May 19 00:00 postgresql-16-main.log.1
    -rw-r-----  1 postgres adm      254K May 12 00:00 postgresql-16-main.log.2.gz
    -rw-r-----  1 postgres adm       18K May  5 00:00 postgresql-16-main.log.3.gz
    -rw-r-----  1 postgres adm      125K Apr 28 00:00 postgresql-16-main.log.4.gz
    -rw-r-----  1 postgres adm       27K Apr 21 00:00 postgresql-16-main.log.5.gz
    -rw-r-----  1 postgres adm       36K Apr 14 00:00 postgresql-16-main.log.6.gz
    -rw-r-----  1 postgres adm      1.1K Apr  7 00:00 postgresql-16-main.log.7.gz
    -rw-r-----  1 postgres adm      2.0K Mar 31 00:00 postgresql-16-main.log.8.gz

    Na verdade, você pode escolher outros lugares arbitrários para colocar seus logs, como em um disco separado (pode ser útil em servidores com muita carga), ajustando a variável log_directory. Abaixo vemos um exemplo da localização do diretório de logs na AWS RDS:

    psql: consultando o diretório de logs em um banco AWS RDS
    SHOW log_directory;
        log_directory
    ----------------------
     /rdsdbdata/log/error

    O valor do log_directory pode ser absoluto, com o path completo ou relativo ao diretório do $PGDATA.

    Subdiretórios

    • base/: contém um subdiretório para cada banco de dados individual no cluster;
      • Dentro desses subdiretórios de cada banco que são identificados com números (16390, por exemplo) temos com os dados de cada objeto no banco, como tabelas e índice. Estes arquivos possuem eventualmente outros com o mesmo número e um sufixo _fsm (free space map) e _vm (visibility map), que são essenciais para manutenção e otimização e controle de transações. Além disso, temos um diretório chamado pgsql_tmp com arquivos temporários como tabelas temporárias. Eventualmente é possível armazenar estes arquivos temporários em outros tablespaces, ajustando a configuração temp_tablespaces;
    • global/: contém tabelas e dados globais, como informações sobre usuários e grupos, por exemplo, as tabelas pg_database e pg_control;
      • Aqui também temos os arquivos _fsm e _vm, mas a nível global, ou seja, que se aplicam a todos os bancos de dados em um cluster;
    • pg_xlog/ ou pg_wal/: contém os arquivos de log de transação (WAL – Write-Ahead Logging), essenciais para a recuperação de dados;
    • pg_clog/ ou pg_commit_ts/: mantém informações sobre o estado das transações atuais;
    • pg_stat/: armazena dados estatísticos permanentes utilizados para otimização de consultas;
    • pg_stat_tmp/: armazena dados estatísticos temporários utilizados para otimização de consultas;
    • pg_tblspc/: pontos de montagem de tablespaces, que permitem a administração de locais de armazenamento adicionais.

    Processos

    PostgreSQL é um sistema de gerenciamento de banco de dados do tipo cliente-servidor. Diferente de outros modelos, ele utiliza uma arquitetura multiprocesso para rodar em um único host. Imagine uma equipe trabalhando em conjunto para cuidar do seu banco de dados: esse é o conceito por trás dessa arquitetura.

    Essa “equipe” é composta por diversos processos, cada um com uma função específica:

    • postmaster (server process): o processo principal do PostgreSQL. É responsável por iniciar, parar e supervisionar todos os processos de servidor do PostgreSQL. Ele recebe conexão de apenas uma porta (a 5432 por padrão), porém podemos ter mais de uma instância do PostgreSQL rodando no mesmo servidor, especificando uma porta para cada instância. Podemos ter, por exemplo, um PostgreSQL na versão 16 na porta 5432 e outro na versão 15 na porta 5433;
    • postgres (backend process): cada conexão ao banco de dados é gerenciada por um processo postgres dedicado. Esse processo é responsável por receber e executar as consultas e comandos enviados pelos clientes, garantindo uma experiência personalizada para cada um;
    • autovacuum launcher (background process): processo responsável pela limpeza automática do banco de dados, removendo registros obsoletos gerados por operações de UPDATE e DELETE;
    • wal writer (background process): gerencia a gravação dos logs de transação (WAL) no disco, garantindo a durabilidade das transações, o que permite a recuperação do banco de dados;
    • wal sender (background process): responsável por enviar os registros do WAL (Write-Ahead Logging) do servidor primário para um ou mais servidores de réplica (standby). Este processo é fundamental para a replicação em tempo real e a alta disponibilidade;
    • wal receiver (background process): é executado em servidores de réplica e recebe os registros do WAL enviados pelo processo WAL sender do servidor primário;
    • statistics collector (background process): é responsável por coletar e agrupar estatísticas sobre a operação do banco de dados, como contagem de linhas lidas, inseridas, atualizadas ou deletadas;
    • logging collector (background process): coleta e armazena mensagens de log do servidor PostgreSQL em arquivos de log;
    • archiver (background process): copia os arquivos do WAL para um local de arquivamento seguro após eles serem preenchidos;
    • bgwriter (background process): é o “background writer”, que periodicamente escreve páginas sujas da memória para o disco, ajudando a manter a consistência dos dados;
    • checkpointer (background process): periodicamente cria pontos de verificação (checkpoints) que ajudam na recuperação de falhas;
    • IPC (background process): os processos do PostgreSQL se comunicam usando IPC (Inter-Process Communication), que inclui sinais (signals), memória compartilhada (shared memory) e semáforos (semaphores). Estes mecanismos permitem a coordenação entre os processos, garantindo a consistência e a integridade dos dados.

    Abaixo segue o comando ps faux | grep postgres executado como root, que é um comando para mostrar todos os processos relacionados ao postgres, de todos os usuários, e sua saída:

    Debian shell: consultando processos postgres
    # ps faux | grep postgres
    postgres  171711  0.0  1.7 217608 17460 ?        Ss   Apr09   7:52 /usr/lib/postgresql/16/bin/postgres -D /var/lib/postgresql/16/main -c config_file=/etc/postgresql/16/main/postgresql.conf
    postgres  171712  0.0  7.8 218036 77644 ?        Ss   Apr09   0:02  \_ postgres: 16/main: checkpointer
    postgres  171713  0.0  0.7 217756  7468 ?        Ss   Apr09   0:41  \_ postgres: 16/main: background writer
    postgres  171715  0.0  0.6 217608  6796 ?        Ss   Apr09   0:44  \_ postgres: 16/main: walwriter
    postgres  171716  0.0  0.7 219208  7108 ?        Ss   Apr09   1:45  \_ postgres: 16/main: autovacuum launcher
    postgres  171717  0.0  0.4 219184  4340 ?        Ss   Apr09   0:02  \_ postgres: 16/main: logical replication launcher

    Como um exemplo adicional, fizemos um teste com o pg_bench (com escala 10x maior do que a padrão, 50 clientes e quatro threads, com duração de 120 segundos) e rodamos, desta vez, o comando top -c, que fornece a visualização dinâmica e interativa dos processos. Este é um trecho da saída:

    Debian shell: consultando processos com teste do pg_bench
    # top -c
    top - 18:01:38 up 57 days,  2:24,  2 users,  load average: 19.95, 5.63, 1.93
    Tasks: 135 total,  32 running, 103 sleeping,   0 stopped,   0 zombie
    %Cpu(s): 64.7 us, 34.3 sy,  0.0 ni,  0.0 id,  1.0 wa,  0.0 hi,  0.0 si,  0.0 st
    MiB Mem :    961.0 total,     77.9 free,    505.3 used,    679.6 buff/cache
    MiB Swap:      0.0 total,      0.0 free,      0.0 used.    455.7 avail Mem
    
        PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
    1219997 postgres  20   0  239676   7792   6412 S  17.5   0.8   0:09.62 /usr/lib/postgresql/16/bin/pgbench -c 50 -j 4 -T 120 postgres
    1220014 postgres  20   0  219816  84068  80660 R   2.3   8.5   0:00.92 postgres: 16/main: postgres postgres [local] idle in transaction
    1220040 postgres  20   0  219820  82300  78888 S   2.0   8.4   0:00.90 postgres: 16/main: postgres postgres [local] UPDATE waiting
    1220050 postgres  20   0  219820  83928  80516 R   2.0   8.5   0:00.90 postgres: 16/main: postgres postgres [local] UPDATE waiting
    1220005 postgres  20   0  219816  80776  77376 R   1.7   8.2   0:00.90 postgres: 16/main: postgres postgres [local] COMMIT
    1220007 postgres  20   0  219816  82044  78636 S   1.7   8.3   0:00.90 postgres: 16/main: postgres postgres [local] UPDATE waiting
    1220009 postgres  20   0  219816  82604  79204 R   1.7   8.4   0:00.90 postgres: 16/main: postgres postgres [local] idle
    1220010 postgres  20   0  219816  82204  78804 S   1.7   8.4   0:00.90 postgres: 16/main: postgres postgres [local] UPDATE waiting
    1220011 postgres  20   0  219816  81988  78588 R   1.7   8.3   0:00.90 postgres: 16/main: postgres postgres [local] idle in transaction
    1220012 postgres  20   0  219816  82944  79544 S   1.7   8.4   0:00.90 postgres: 16/main: postgres postgres [local] UPDATE waiting
    1220013 postgres  20   0  219816  83216  79800 R   1.7   8.5   0:00.90 postgres: 16/main: postgres postgres [local] idle in transaction
    1220015 postgres  20   0  219816  84092  80692 S   1.7   8.5   0:00.90 postgres: 16/main: postgres postgres [local] UPDATE waiting
    1220017 postgres  20   0  219816  80868  77468 R   1.7   8.2   0:00.90 postgres: 16/main: postgres postgres [local] UPDATE waiting
    1220018 postgres  20   0  219816  83108  79708 R   1.7   8.4   0:00.90 postgres: 16/main: postgres postgres [local] idle in transaction
    1220021 postgres  20   0  219816  83968  80568 S   1.7   8.5   0:00.90 postgres: 16/main: postgres postgres [local] UPDATE waiting
    1220022 postgres  20   0  219816  79704  76304 S   1.7   8.1   0:00.90 postgres: 16/main: postgres postgres [local] UPDATE waiting
    1220023 postgres  20   0  219816  82652  79252 R   1.7   8.4   0:00.90 postgres: 16/main: postgres postgres [local] idle in transaction
    1220024 postgres  20   0  219820  82452  79036 S   1.7   8.4   0:00.90 postgres: 16/main: postgres postgres [local] UPDATE waiting
    1220025 postgres  20   0  219820  80704  77292 R   1.7   8.2   0:00.90 postgres: 16/main: postgres postgres [local] COMMIT
    1220026 postgres  20   0  219820  79340  75928 R   1.7   8.1   0:00.90 postgres: 16/main: postgres postgres [local] idle in transaction

    O primeiro processo pgbench é o que está executando o teste de benchmarking e consome 17,5% da CPU, sendo o responsável pela maior carga no sistema.

    Também é possível observar vários processos em estado R (running) ou S (sleeping), o que é esperado em um ambiente de testes. Também temos os status idle in transaction e UPDATE waiting, indicando que estão aguardando a conclusão de transações ou de operações de atualização.

    Conclusão

    O PostgreSQL tem uma organização relativamente simples de arquivos, diretórios e processos. Para o desenvolvedor ou usuário final do banco de dados, essas informações podem ser mera curiosidade, mas para quem pretende ser um DBA, a história é bem diferente. É fundamental entender o que cada um faz e onde fica cada informação. Enquanto tudo estiver funcionando bem, você provavelmente não vai se preocupar muito com isso, mas quando problemas ocorrerem, como falhas de hardware, bugs etc., entender esses conceitos básicos será fundamental para resolver os problemas. 
    Mais informações podem ser encontradas na documentação oficial, aqui.

    No próximo artigo, vamos entender um pouco melhor como os objetos do banco são organizados logicamente no catálogo interno e fisicamente nos diretórios $PGDATA/base, $PGDATA/global e $PGDATA/pg_tblspace.

  • O mínimo sobre segurança que você precisa saber

    O mínimo sobre segurança que você precisa saber

    Introdução

    Em um cenário onde a segurança dos dados é fundamental, a gestão adequada das permissões de acesso em um banco de dados é essencial. Ao concedermos e revogarmos permissões de acesso aos objetos do banco, assumimos um controle preciso sobre quem pode realizar quais operações e onde. Esse controle é particularmente crucial em ambientes de produção, onde a integridade e a segurança dos dados são prioritárias.

    Ao estabelecermos usuários com papéis bem definidos e acesso restrito, fortalecemos as defesas contra possíveis ameaças à segurança. Essa abordagem não apenas protege os dados sensíveis, mas também reduz o risco de acessos não autorizados e potenciais danos ao banco de dados.

    Neste artigo, exploraremos a importância das permissões de acesso, papéis de usuários e como gerenciar essas permissões com os comandos GRANT e REVOKE.

    É importante destacar que, para melhor entendimento da mudança de papéis ao longo dos exemplos, estamos usando o prompt do psql com uma personalização, com a seguinte configuração (dentro do arquivo psqlrc; mais informações no artigo Utilização do psql):

    Debian shell: conteúdo do arquivo psqlrc
    \set PROMPT1 '%n@%/%R%#%x '

    Essa configuração exibe o nome do usuário na sessão atual, seguido de um “@”, e o nome da base de dados.

    Roteiro:

    • GRANT / REVOKE:
      • Aplicação em SCHEMA;
      • Aplicação em TABLE e SEQUENCE;
      • Aplicação em COLUMN;
      • Aplicação em ROLE;
      • Aplicação em DATABASE;
      • Aplicação em FUNCTION / PROCEDURE.
    • ALTER DEFAULT PRIVILEGES
    • DROP OWNED / REASSIGN OWNED

    GRANT / REVOKE

    Os comandos GRANT e REVOKE são usados para conceder e revogar permissões de acesso a objetos de banco de dados, como tabelas, esquemas, funções e sequências. Eles desempenham um papel fundamental na gestão de segurança e controle de acesso no banco de dados.

    O comando GRANT é usado para conceder permissões de acesso a usuários ou roles específicas em objetos de banco de dados. As permissões comumente concedidas incluem SELECT, INSERT, UPDATE, DELETE, USAGE, EXECUTE entre outras, e podem ser concedidas para objetos individuais ou para todos os objetos de um determinado tipo em um esquema. Por exemplo, você pode conceder a um usuário permissão para ler dados de uma tabela específica usando o seguinte comando:

    Comandos SQL: concedendo um privilégio de tabela
     GRANT SELECT ON tabela TO usuário;

    Por outro lado, o comando REVOKE é usado para revogar permissões previamente concedidas. Ele permite remover permissões de acesso de usuários ou roles em objetos de banco de dados específicos, restringindo acessos. Por exemplo, você pode revogar a permissão de um usuário para modificar dados em uma tabela usando o comando a seguir:

    Comandos SQL: revogando um privilégio em uma tabela
     REVOKE UPDATE ON tabela FROM usuário;

    Considerando nossas roles, schemas e usuários criados no artigo anterior, abaixo vamos exemplificar a aplicação de GRANT e REVOKE em diversos objetos para melhor entendimento, sempre utilizando o superusuário, que no nosso caso é o administrador do banco de dados (dba) para conceder e revogar privilégios.

    Aplicação em SCHEMA

    Aqui vamos conceder permissão de uso (USAGE) do schema aplicacao1 para os usuários app1_admin, app1_user1 e app1_user2, que possibilita consultar os objetos desse schema, por exemplo, as tabelas que criamos:

    psql shell: concedendo privilégio USAGE em um schema
    dba@aplicacao=# GRANT USAGE ON SCHEMA aplicacao1 TO app1_user1;
    
    app1_user1@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
     id | nome  | idade
    ----+-------+-------
      1 | João  | 30
      2 | Maria | 25
      3 | Pedro | 40
    (3 rows)

    Antes de rodarmos o mesmo comando para o usuário app1_user2, confira o exemplo da tentativa de consulta da mesma tabela sem permissão no schema:

    psql shell: tentando consultar dados sem privilégio em uma tabela
    app1_user2@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
    ERROR:  permission denied for schema aplicacao1
    LINE 1: SELECT * FROM aplicacao1.tabela1_app1;

    Como esse privilégio concedido diz especificamente sobre a possibilidade de consultar os objetos do esquema, não é possível modificá-los, como tentar inserir dados:

    psql shell: tentanto inserir dados sem privilégio em uma tabela
    app1_user1@aplicacao=>  INSERT INTO aplicacao1.tabela1_app1 (nome, idade) VALUES
    ('Novo nome', '0');
    ERROR:  permission denied for table tabela1_app1

    Isso será possível quando concedermos permissões a níveis de tabela, no próximo tópico.

    Aplicação em TABLE e SEQUENCE

    Agora, vamos conceder a permissão de consulta, inserção, atualização e exclusão de dados (SELECT, INSERT, UPDATE, DELETE, respectivamente) ao usuário app1_admin nas tabelas do schema aplicacao1 (tabela1_app1, tabela2_app1, tabela3_app1):

    psql shell: concedendo privilégios de várias tabelas
    dba@aplicacao=# GRANT SELECT, INSERT, UPDATE, DELETE ON aplicacao1.tabela1_app1 TO app1_admin;
    GRANT SELECT, INSERT, UPDATE, DELETE ON aplicacao1.tabela2_app1 TO app1_admin;
    GRANT SELECT, INSERT, UPDATE, DELETE ON aplicacao1.tabela3_app1 TO app1_admin;

    Outra forma de fazer isso seria usando a cláusula ALL TABLES IN SCHEMA, o que facilita o processo encurtando o comando, portanto, aqui vamos conceder as permissões para o usuário app2_admin sobre as tabelas do schema aplicacao2 e a permissão de leitura para os demais usuários:

    psql shell: concedendo privilégios da cláusula ALL TABLES IN SCHEMA
    dba@aplicacao=# GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA aplicacao1 TO app1_admin;
    dba@aplicacao=# GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA aplicacao2 TO app2_admin;
    dba@aplicacao=# GRANT SELECT ON ALL TABLES IN SCHEMA aplicacao2 TO app1_user1;
    dba@aplicacao=# GRANT SELECT ON ALL TABLES IN SCHEMA aplicacao2 TO app1_user2;
    dba@aplicacao=# GRANT SELECT ON ALL TABLES IN SCHEMA aplicacao2 TO app2_user1;
    dba@aplicacao=# GRANT SELECT ON ALL TABLES IN SCHEMA aplicacao2 TO app2_user2;

    Além disso, para possibilitar que a inserção de dados ocorra bem, também precisamos conceder permissão de USAGE na sequência das tabelas, por exemplo, ‘tabela1_app1_id_seq’, por se tratar de uma tabela com uma coluna SERIAL (com o id de cada linha se autoincrementando). Para isso, vamos rodar:

    psql shell: concedendo privilégio USAGE em sequências de uma tabela
    dba@aplicacao=# GRANT USAGE ON SEQUENCE aplicacao1.tabela1_app1_id_seq TO app1_admin;

    Sendo assim, o administrador da aplicação torna-se o único usuário que consegue inserir novas linhas nas tabelas selecionadas:

    psql shell: inserindo dados em uma tabela
    app1_admin@aplicacao=> INSERT INTO aplicacao1.tabela1_app1 (nome, idade) VALUES
    (Novo nome', '0');
    
    app1_admin@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
     id |    nome    | idade
    ----+------------+-------
      1 | João       | 30
      2 | Maria      | 25
      3 | Pedro      | 40
      4 | Novo nome  | 0
    (4 rows)

    Por consequência, caso o administrador da aplicação 2 (app2_admin) tente consultar ou realizar qualquer outra transação no schema da aplicação 1, haverá erro:

    psql shell: tentativa de consulta sem privilégio em um schema
    app2_admin@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
    ERROR:  permission denied for schema aplicacao1
    LINE 1: SELECT * FROM aplicacao1.tabela1_app1;

    Em outro exemplo, temos uma tabela (aplicacao1.tabela3_app1) que estritamente não pode ser editada, e para esse controle iremos revogar a permissão de edição da role public, que engloba todos os usuários:

    psql shell: revogando permissão de edição de todos os usuários em uma tabela
    dba@aplicacao=# REVOKE DELETE, UPDATE ON aplicacao1.tabela3_app1 FROM public;
    REVOKE

    Assim, caso haja tentativa de edição nessa tabela por qualquer usuário além do DBA, ocorrerá o seguinte erro:

    psql shell: tentativa de edição sem privilégio de uma tabela
    app1_user1@aplicacao=> INSERT INTO aplicacao1.tabela3_app1 (email)
    VALUES ('exemplo@example.com');
    ERROR:  permission denied for table tabela3_app1
    Time: 1.684 ms

    Aplicação em COLUMN

    Agora, vamos considerar uma situação em que o usuário app1_user1 precisa atualizar uma linha específica em uma das tabelas da aplicação 1, mas apenas em uma coluna em especial. Por exemplo, suponha que o usuário precise atualizar a idade de uma pessoa na tabela tabela1_app1.

    Vamos conceder permissões apropriadas para que o usuário app1_user1 possa realizar essa operação:

    psql shell: concedendo privilégio de edição de uma coluna
    dba@aplicacao=# GRANT UPDATE (idade) ON aplicacao1.tabela1_app1 TO app1_user1;

    Após conceder essa permissão, o usuário app1_user1 será capaz de atualizar a coluna idade na tabela tabela1_app1, mas não terá permissão para atualizar outras colunas ou realizar outras operações na tabela.

    Por exemplo, o usuário app1_user1 pode executar a seguinte operação:

    psql shell: editano dados de uma coluna específica
    app1_user1@aplicacao=> UPDATE aplicacao1.tabela1_app1 SET idade = '35' WHERE id = 1;
    app1_user1@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
     id |    nome    | idade
    ----+------------+-------
      2 | Maria      | 25
      3 | Pedro      | 40
      4 | Novo nome  | 0
      1 | João       | 35
    (4 rows)

    No entanto, se ele tentar atualizar outras colunas ou realizar outras operações, como inserção ou exclusão de linhas, ele receberá uma mensagem de erro informando que a permissão é negada para essa ação específica:

    psql shell: tentativa de edição de uma tabela sem privilégio
    app1_user1@aplicacao=> INSERT INTO aplicacao1.tabela1_app1 (nome, idade) VALUES ('Joana', '28');
    ERROR:  permission denied for table tabela1_app1

    E, após a modificação necessária, é possível remover essa permissão do usuário com o REVOKE:

    psql shell: recvgando privilégio de edição da coluna
    dba@aplicacao=# REVOKE UPDATE (idade) ON aplicacao1.tabela1_app1 FROM app1_user1;

    No entanto, isso impossibilita novas alterações de idade por esse usuário:

    psql shell: tentativa de edição da coluna sem privilégio
    app1_user1@aplicacao=>  UPDATE aplicacao1.tabela1_app1 
    SET idade = '30' 
    WHERE id = 1;
    ERROR:  permission denied for table tabela1_app1

    Esse é um exemplo simples de como conceder permissões em nível de coluna para usuários específicos, permitindo que eles realizem operações precisas em dados específicos em uma tabela. Isso proporciona um controle granular sobre o que os usuários podem fazer em um banco de dados.

    Aplicação em ROLE

    Para exemplificar o controle de acesso utilizando role como um papel e não como um usuário, vamos criar uma nova role chamada editores_app1, que vai possibilitar que alguém se torne editor das tabelas do schema aplicacao1, tendo como administrador o usuário app1_admin; em seguida, adicionaremos o usuário app1_user2 nessa role.
    Para criar a role:

    psql shell: criando uma role com administrador
    dba@aplicacao=# CREATE ROLE editores_app1 WITH ADMIN app1_admin;

    Depois, vamos dar a permissão de edição nas tabelas para a role criada:

    psql shell: concedendo privilégio em todas as tabelas do schema para role específica
    dba@aplicacao=# GRANT UPDATE ON ALL TABLES IN SCHEMA aplicacao1 TO editores;

    Agora, podemos adicionar o usuário app1_user2 nessa role:

    psql shell: adicionando usuário em role
    dba@aplicacao=# GRANT editores TO app1_user2;

    Então, podemos editar o que for necessário com esse usuário (abaixo vemos uma consulta à tabela antes e pós-edição):

    psql shell: consultando uma tabela , editando um dado e consultando novamente
    app1_user2@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
     id |    nome    | idade
    ----+------------+-------
      2 | Maria      | 25
      3 | Pedro      | 40
      4 | Novo nome  | 0
      1 | João       | 35
    (4 rows)
    
    app1_user2@aplicacao=> UPDATE aplicacao1.tabela1_app1
    SET nome = 'João Vitor'
    WHERE id = 1;
    UPDATE 1
    
    app1_user2@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
     id |    nome    | idade
    ----+------------+-------
      2 | Maria      | 25
      3 | Pedro      | 40
      4 | Novo nome  | 0
      1 | João Vitor | 35
    (4 rows)

    Após feita a alteração, também é possível remover esse usuário da role criada por meio do REVOKE:

    psql shell: revogando de um usuário privilégios de uma role
    dba@aplicacao=# REVOKE editores FROM app1_user2;
    REVOKE ROLE

    Como pudemos ver, utilizando roles, simplificamos o controle de acesso ao atribuir permissões específicas a papéis, gerenciando ambientes com vários usuários de maneira mais ampla e eficiente.

    Aplicação em DATABASE

    A concessão de permissões a nível de DATABASE oferece controle sobre todas as tabelas e objetos contidos dentro do banco de dados específico. Esse método é útil quando se deseja aplicar permissões de forma abrangente, abordando todas as tabelas e esquemas presentes no banco de dados.

    Para exemplificar, consideremos o cenário em que precisamos revogar a permissão para conexão com os bancos de dados de um usuário. Vamos supor que o usuário app1_user1 não possa mais se conectar com nosso servidor por motivos de segurança. Para remover o acesso do usuário app1_user1, considerando a permissão de CONNECT, devemos começar revogando essa permissão da role public, que é uma role padrão do PostgreSQL, gerada automaticamente quando criamos um banco (também existe um schema padrão de mesmo nome, que todos os usuários podem mexer e acessar). Para isso, vamos rodar o seguinte comando:

    psql shell: revogando privilégio de conexão de todos os usuários
    dba@aplicacao=# REVOKE CONNECT ON DATABASE aplicacao FROM public;

    Dessa forma, não é mais possível se conectar ao banco aplicacao. Para resolver isso, vamos conceder essa permissão separadamente para cada usuário que desejamos, desconsiderando, então, o usuário app1_user1:

    psql shell: concedendo privilégio de conexão para vários usuários
    dba@aplicacao=# GRANT CONNECT ON DATABASE aplicacao TO app1_admin, app1_user2, app2_admin, app2_user1, app2_user2;

    Sendo assim, caso queiramos revogar o acesso de outro usuário, basta revogar a permissão CONNECT ao database aplicacao da role/usuário desejado. Essa é a visualização de erro na tentativa de acesso do usuário que não possui a permissão CONNECT:

    psql shell: tentativa de conexão com o banco sem permissão
    $ psql -h localhost -p 5432 -U app1_user1 -d aplicacao;
    Password for user app1_user1:
    psql: error: connection to server at "localhost" (::1), port 5432 failed: FATAL:  permission denied for database "aplicacao"
    DETAIL:  User does not have CONNECT privilege.

    Aplicação em FUNCTION/PROCEDURE

    Vamos, agora, exemplificar o gerenciamento do acesso as functions concedendo permissão de execução para a função last_day, que pertence ao banco pagila (exemplo de banco de dados disponibilizado pelo PostgreSQL para fins educacionais e de treinamento). 

    Para isso, o primeiro passo é revogar todas as permissões em relação às funções da role padrão public (aquele grupo em que todos os usuários estão), especificando o schema onde se encontra a função que desejamos gerenciar, no nosso caso, o schema pagila:

    psql shell: revogando de todos os usuários o privilégio de todas as permissões de uma função
    postgres@pagila=# REVOKE ALL ON FUNCTION schema_pagila.last_day FROM public;
    REVOKE

    Feito isso, assim como para acesso ao database, precisamos especificar quais usuários podem executar essa função explicitamente, já que removemos a permissão de todos ao revogar a role public. Então, para garantir que nosso usuário user_pagila possa executar essa função, podemos rodar:

    psql shell: concedendo privilégio de execução de uma função para um usuário
    postgres@pagila=# GRANT EXECUTE ON FUNCTION last_day TO user_pagila;
    GRANT

    Pronto! Para os usuários que não foram contemplados com a permissão, aparecerá a seguinte mensagem de erro:

    psql shell: tentativa sem privilégio de execução de uma função
    user_pagila2@pagila=> SELECT schema_pagila.last_day(current_date);
    ERROR:  permission denied for function last_day

    ALTER DEFAULT PRIVILEGES

    O comando ALTER DEFAULT PRIVILEGES oferece uma maneira de configurar os privilégios padrão que serão aplicados a objetos criados no futuro. Em outras palavras, ele permite definir as permissões que novos objetos herdarão automaticamente assim que forem criados. 

    Ao contrário do que muitos podem pensar, esse comando não afeta os privilégios de objetos existentes, apenas os novos objetos que serão criados após a configuração. Você pode definir privilégios padrão globalmente para todos os objetos criados no banco de dados atual ou especialmente para objetos criados em schemas específicos.

    Por padrão, normalmente, os privilégios de novos objetos são herdados do papel de usuário que os cria. No entanto, o comando ALTER DEFAULT PRIVILEGES permite personalizar esses privilégios de acordo com as necessidades do ambiente. Por exemplo, você pode conceder permissões de leitura para o público em geral em todas as tabelas futuras criadas em um determinado esquema ou permitir que um papel específico execute funções recém-criadas.

    Para exemplificar, vamos criar mais dois usuários, o app1_user3 e o app_user4:

    psql shell: criando usuários
    dba@aplicacao=# CREATE ROLE app1_user3 LOGIN PASSWORD 'senha';
    dba@aplicacao=# CREATE ROLE app1_user4 LOGIN PASSWORD 'senha';

    E, claro, conceder a eles o uso do schema aplicacao1, como também a permissão para se conectarem com a base de dados aplicacao:

    psql shell: concedendo privilégio de uso de um schema para os dois novos usuários
    dba@aplicacao=# GRANT USAGE ON SCHEMA aplicacao1 TO app1_user3;
    dba@aplicacao=# GRANT USAGE ON SCHEMA aplicacao1 TO app1_user4;
    psql shell: concedendo privilégio de conexão a um banco para os dois novos usuários
    dba@aplicacao=# GRANT CONNECT ON DATABASE aplicacao TO app1_user3;
    dba@aplicacao=# GRANT CONNECT ON DATABASE aplicacao TO app1_user4;

    Então, vamos usar o comando ALTER DEFAULT PRIVILEGES para definir, como padrão, a possibilidade de consulta nas novas tabelas criadas para o usuário app1_user3:

    psql shell: alterando padrão de privilégios com inclusão de consulta de tabelas no schema
    dba@aplicacao=# ALTER DEFAULT PRIVILEGES IN SCHEMA aplicacao1 GRANT SELECT ON TABLES TO app1_user3;

    Para testarmos, vamos criar uma nova tabela no schema aplicacao1, chamada tabela4_app1, inserindo, nela, dados de exemplo:

    psql shell: criando uma nova tabela no schema e inserindo dados nela
    dba@aplicacao=# CREATE TABLE aplicacao1.tabela4_app1 (
        id SERIAL PRIMARY KEY,
        dado VARCHAR(150)
    );
    CREATE TABLE
    
    dba@aplicacao=# INSERT INTO aplicacao1.tabela4_app1 (dado)
    VALUES ('algum dado');
    INSERT 0 1

    Assim, é possível que o usuário app1_user3 consulte as informações da tabela por mais que o comando de GRANT SELECT não tenha sido rodado explicitamente; isso graças ao ALTER DEFAULT PRIVILEGES rodado antes da criação da tabela, que também servirá para futuros objetos caso criemos mais:

    psql shell: consultando dados da nova tabela por um usuário com privilégio fornecido pelo ALTER DEFAULT PRIVILEGES
    app1_user3@aplicacao=> SELECT * FROM aplicacao1.tabela4_app1;
     id |    dado
    ----+------------
      1 | algum dado
    (1 row)

    Em contrapartida, o outro usuário, o app1_user4, não pode fazer a consulta porque não rodamos o ALTER DEFAULT PRIVILEGES para ele e nem o GRANT SELECT de forma explícita para essa tabela:

    psql shell: tentativa de consulta de dados de uma tabela sem privilégios para isso
    app1_user4@aplicacao=> SELECT * FROM aplicacao1.tabela5_app1;
    ERROR:  permission denied for table tabela5_app1

    DROP OWNED / REASSIGN OWNED

    Os comandos DROP OWNED e REASSIGN OWNED são usados para gerenciar a propriedade dos objetos do banco de dados, sendo úteis em situações de manutenção de usuários. Enquanto o DROP OWNED remove todos os objetos pertencentes a uma role específica e revoga suas permissões, o REASSIGN OWNED transfere a propriedade desses objetos para outra role, sem removê-los.

    DROP OWNED

    O comando DROP OWNED é utilizado para remover todos os objetos (como tabelas, sequências, funções etc.) que pertencem a uma ou mais roles específicas, além de revogar todas as permissões que essas roles possuem em outros objetos do banco de dados. É frequentemente usado antes de excluir um usuário ou uma role, garantindo que não restem objetos associados ou permissões residuais.

    Por exemplo, suponha que você precise remover todos os objetos pertencentes à role app1_admin. Para isso, você pode usar o seguinte comando:

    Comandos SQL: removendo objetos pertencentes a um usuário
    DROP OWNED BY app1_admin CASCADE;

    Isso removerá todos os objetos que pertencem à app1_admin e revogará todas as permissões associadas, incluindo a remoção de qualquer objeto que dependa dos que pertencem a essa role.

    Outra opção desse comando é a RESTRICT, que impede a remoção de objetos caso existam dependências, ou seja, outros objetos que dependam daqueles que pertencem à role especificada. Essa opção é útil quando você deseja garantir que não está removendo inadvertidamente objetos importantes que poderiam afetar o funcionamento do banco de dados. Veja um exemplo de erro de dependência:

    psql shell: erro na remoção de objetos por dependência
    ERROR:  cannot drop objects owned by role because other objects depend on them
    DETAIL:  Table "tabela_dependente" depends on table "tabela1_app1".

    Esse erro pode ser resolvido com a RESTRICT:

    Comandos SQL: removendo objetos com restrição daqueles com dependência
    DROP OWNED BY app1_user2 RESTRICT;

    REASSIGN OWNED

    O comando REASSIGN OWNED permite transferir a propriedade de todos os objetos pertencentes a uma role para outra, servindo para quando um usuário ou uma role estão prestes a serem removidos, mas seus objetos associados ainda precisam ser mantidos, só que sob a propriedade de outra role. 

    Por exemplo, para transferir todos os objetos da app1_admin para dba, podemos utilizar o seguinte comando:

    Comandos SQL: transferindo propriedade de objetos de um usuário para outro
    REASSIGN OWNED BY app1_admin TO dba;

    Com esse comando, todos os objetos que pertenciam à role app1_admin agora passam a ser de propriedade da role dba, garantindo a continuidade de acesso e administração.

    Conclusão

    Lidar com segurança é chato; não tem adjetivo melhor que esse para descrever o trabalho de dar permissões de tabela em tabela para cada usuário. Ninguém gosta, nem os mais perfeccionistas. No entanto, em tempos de bancos de dados na nuvem, isso é importantíssimo. Colocamos este capítulo logo no começo para que você se acostume a lidar com esse tipo de coisa desde já. Acredite, dói menos assim. 

    O PostgreSQL tem um jeitinho aqui e ali de facilitar as coisas, mas se você não começar a pensar em segurança logo no início, depois vai ficar bem mais difícil e, sem dúvida, mais tedioso. Pense nisso agora. E pense nisso em todos os ambientes de trabalho, seja durante o desenvolvimento, seja em testes, homologação etc. Se deixar para ver isso só quando chegar no ambiente de produção, com certeza você terá problemas e receberá comentários desagradáveis quando sofrer uma auditoria de segurança ou, pior, quando sofrer uma tentativa de invasão no seu banco de dados. Ninguém quer que isso aconteça, claro, mas a questão não é se isso um dia vai acontecer, mas quando.

  • Utilização de SQL básico

    Utilização de SQL básico

    Introdução

    Já percorremos um bom caminho até aqui. Instalamos o PostgreSQL em diferentes cenários, vimos algumas ferramentas para utilização no dia a dia, mas você já deve estar um pouco cansado disso e louco para colocar a mão na massa, não é? Então, neste artigo, exploraremos os principais comandos e conceitos envolvidos na administração e manipulação de dados em um ambiente SQL, mostrando comandos que podem ser executados no psql, desde a criação e o gerenciamento de papéis/usuários (roles) até a manipulação dos principais objetos (tabelas, esquemas e bases de dados), com muitos exemplos. A ideia é dar uma visão geral, para você se sentir mais confiante e sair explorando possibilidades para seus próprios projetos independentemente dos próximos artigos na sequência. Boa diversão!

    Roteiro:

    • CREATE / ALTER / DROP …
      • … ROLE
      • … DATABASE
      • … SCHEMA
      • … TABLE
    • INSERT
    • SELECT
    • UPDATE 
    • DELETE

    CREATE / ALTER / DROP

    Os comandos CREATE, ALTER e DROP são fundamentais para a administração de bancos de dados em SQL, sendo responsáveis pela criação, alteração e exclusão de objetos, respectivamente. Em termos mais formais, esses comandos correspondem a um subconjunto da linguagem SQL conhecida como DDL: Linguagem de definição de dados (do Inglês Data Definition Language).

    CREATE: como dito anteriormente, esse comando é utilizado para criar objetos dentro do banco de dados. Isso inclui a criação de tabelas, bancos de dados, esquemas, usuários, papéis (roles), entre outros. 

    ALTER: esse comando é utilizado para modificar a estrutura de objetos existentes no banco de dados. Ele permite alterar tabelas, adicionar ou remover colunas, modificar tipos de dados, entre outras operações.

    DROP: é utilizado para excluir objetos do banco de dados. Isso pode incluir a exclusão de tabelas, bancos de dados, esquemas, usuários, entre outros. É uma operação irreversível e deve ser usada com cuidado, pois excluir um objeto pode resultar na perda permanente de dados.

    Exemplos

    Aplicação em ROLE

    O comando CREATE ROLE é utilizado para criar usuários dentro do banco de dados. Um grupo de usuários ou role é uma entidade que define um conjunto de permissões e privilégios na utilização desse banco. Essas permissões podem ser atribuídas a usuários específicos ou a grupos, facilitando a gestão de acesso e segurança. Ao criar uma nova role com o comando CREATE ROLE, você pode especificar diversas configurações, como permissões específicas, restrições de acesso, entre outros. Por exemplo:

    Comandos SQL: criando uma nova role
    CREATE ROLE meu_role LOGIN PASSWORD 'senha';

    Nesse exemplo dado, foi fornecida permissão para fazer login (LOGIN) com as credenciais fornecidas (nome da role e senha, identificada como PASSWORD), portanto, pode ser considerado uma role de usuário. Caso fosse criado sem permissão de login, seria uma role para controle de privilégios, que poderia ser aplicado em diversos usuários. Além disso, é válido lembrar que também temos o comando CREATE USER para criar usuários, que por debaixo dos panos chama o CREATE ROLE com a permissão de login inclusa, tornando-o, assim, específico para usuários (note que o uso do comando CREATE USER está depreciado, por isso não recomendamos a sua utilização). Existem, ainda, muitas outras permissões possíveis, que podem ser consultadas na página oficial do comando CREATE ROLE. Algumas das principais são:

    • SUPERUSER: define se a role terá privilégios de superusuário, que incluem acesso irrestrito a todos os objetos e comandos no banco de dados. É um acesso sensível pelo tanto de privilégios que tem e só deve ser usado em caso de necessidade, por exemplo, para um usuário que fará papel de administrador;
    • CREATEDB: define se a role pode criar bases de dados;
    • CREATEROLE: define se a role pode criar outras roles.

    Para exemplificar esse e outros comandos do SQL, bem como suas permissões e níveis de acesso, criamos as seguintes roles:

    • dba: será o administrador do banco de dados, criado como SUPERUSER;
    • Aplicação 1:
      • app1_admin: será o administrador da aplicação1, criado com ADMIN;
      • app1_user1: será um usuário representando um módulo da aplicação 1;
      • app1_user2: será outro usuário representando mais um módulo da aplicação 1.
    • Aplicação 2:
      • app2_admin: será o administrador da aplicação 1;
      • app2_user1: será um usuário representando um módulo da aplicação 2;
      • app2_user2: será outro usuário representando mais um módulo da aplicação 2.

    Perceba que para as roles de administradores, vamos conceder permissões extras além de apenas o login. Os comandos utilizados foram:

    Comandos SQL: criando novas roles
    CREATE ROLE dba SUPERUSER LOGIN PASSWORD 'senha';
    CREATE ROLE app1_admin CREATEDB CREATEROLE LOGIN PASSWORD 'senha';
    CREATE ROLE app2_admin CREATEDB CREATEROLE LOGIN PASSWORD 'senha';
    CREATE ROLE app1_user1 LOGIN PASSWORD 'senha1';
    CREATE ROLE app1_user2 LOGIN PASSWORD 'senha2';
    CREATE ROLE app2_user1 LOGIN PASSWORD 'senha1';
    CREATE ROLE app2_user2 LOGIN PASSWORD 'senha2';

    Agora, para exemplificar o ALTER ROLE, podemos usar o seguinte cenário: suponha que a role app1_user2 foi criado sem permissão de login, e que isso inviabilizaria o uso do banco. Para corrigir, poderíamos rodar:

    Comandos SQL: concedendo permissão de login para a role app1_user2
    ALTER ROLE app1_user2 LOGIN;

    E isso é válido para qualquer permissão, tanto para acrescentar quanto para removê-la. Claro que, ao se tratar de permissões mais sensíveis, apenas um superusuário pode editar.

    Também é possível adicionar/remover um usuário de uma role com o comando ALTER ou adicionar/remover uma role de um grupo de usuários.

    Para remover uma role, basta usar o comando DROP ROLE da seguinte forma:

    Comandos SQL: removendo uma role
    DROP ROLE app_user3;

    Aplicação em DATABASE

    O comando CREATE DATABASE permite a criação de novos bancos de dados, possibilitando que diferentes conjuntos de dados sejam segregados e gerenciados de forma independente, melhorando a organização e a segurança do sistema.

    Um exemplo simples de utilização do CREATE DATABASE é mostrado abaixo:

    Comandos SQL: criando um novo database
    CREATE DATABASE novo_banco;

    O comando CREATE DATABASE também possui diversas opções e configurações que podem ser especificadas durante a criação do banco de dados. Por exemplo, é possível definir a codificação de caracteres, o proprietário do banco de dados, as permissões de acesso e outras configurações relevantes para o ambiente de armazenamento de dados, o que pode ser conferido nesta página
    Além disso, como citado anteriormente, não é todo usuário que consegue criar um banco novo, é preciso ter permissão para isso, por ser uma operação de maior impacto. 
    Para prosseguir com nosso exemplo de banco, vamos criar a base de dados tendo nossos administradores (app1_admin e app2_admin) como proprietários, tudo feito através do usuário dba:

    CREATE DATABASE aplicacao1 OWNER app1_admin;
    CREATE DATABASE aplicacao2 OWNER app2_admin;

    Utilizando o comando ALTER DATABASE, podemos modificar as configurações de um banco de dados existente, como alterar o proprietário:

    Comandos SQL: alterando o dono do database
    ALTER DATABASE meu_banco OWNER TO novo_proprietario;

    E o DROP DATABASE segue sua mesma função de excluir permanentemente esse objeto:

    Comandos SQL: removendo um database
    DROP DATABASE meu_banco;

    Aplicação em SCHEMA

    O comando CREATE SCHEMA possibilita a criação de espaços de nomes lógicos que contêm objetos relacionados, como tabelas, índices, visualizações e outros schemas. Essa funcionalidade é essencial para organizar e estruturar os objetos dentro de um banco de dados, melhorando a manutenção e a legibilidade do sistema. Um exemplo de utilização seria:

    Comandos SQL: criando um shema
    CREATE SCHEMA novo_esquema;

    Assim como os outros usos do CREATE, aqui também temos diversas opções e configurações que podem ser especificadas, como definição de proprietário e permissões de acesso para usuários, que podem ser conferidas na página oficial do comando CREATE SCHEMA. Para prosseguir com nosso exemplo principal, vamos criar os schemas aplicacao1 e aplicacao2, pertencentes aos usuário app1_admin e app2_admin respectivamente:

    Comandos SQL: criando dois novos schemas e os atribuindo a duas roles
    CREATE SCHEMA aplicacao1 AUTHORIZATION app1_admin;
    CREATE SCHEMA aplicacao2 AUTHORIZATION app2_admin;

    Para modificar configurações de um schema existente, utilizamos o ALTER SCHEMA, conforme exemplo de alteração do proprietário:

    Comandos SQL: alterando o proprietário do schema
    ALTER SCHEMA meu_schema OWNER TO novo_proprietario;

    E para excluí-lo é só utilizar o DROP SCHEMA:

    Comandos SQL: removendo o schema
    DROP SCHEMA meu_schema;

    Aplicação em TABLE

    O comando CREATE TABLE é utilizado para criar novas tabelas em um banco de dados. Como exemplo:

    Comandos SQL: criando uma nova tabela
    CREATE TABLE aplicacao1.tabela1_app1 (
        id SERIAL PRIMARY KEY,
        nome VARCHAR(150),
        idade VARCHAR(150)
    );

    Nesse exemplo, estamos criando uma nova tabela chamada tabela_app1, no schema aplicacao1, com três colunas: id, nome e e-mail. A coluna id é definida como uma chave primária usando o tipo de dado SERIAL, que é um tipo autoincrementável, garantindo valores únicos para cada registro. Os campos nome e e-mail são definidos como VARCHAR, que é o tipo de dado ideal para armazenar strings de tamanho variável. Além disso, a tabela foi criada no database aplicacao1, com o usuário dba, e além dela vamos criar mais duas tabelas, totalizando três tabelas em cada database:

    Comandos SQL: criando uma nova tabela
    CREATE TABLE aplicacao1.tabela2_app1 (
        id SERIAL PRIMARY KEY,
        endereco VARCHAR(150)
    );
    Comandos SQL: criando mais uma tabela
    CREATE TABLE aplicacao1.tabela3_app1 (
        id SERIAL PRIMARY KEY,
        email VARCHAR(150)
    );

    Replicamos as mesmas tabelas para o schema aplicacao2.

    O comando CREATE TABLE, assim como seus similares, também possui diversas opções e configurações que podem ser especificadas durante a criação da tabela. Por exemplo, é possível definir restrições de chave estrangeira, índices, gatilhos e outras propriedades específicas da tabela. Para mais informações é só consultar a página oficial.

    Podemos também adicionar/modificar uma tabela existente utilizando o comando ALTER TABLE, por exemplo, para adicionar uma coluna nova:

    Comandos SQL: alterando a tabela inserindo uma nova coluna
    ALTER TABLE minha_tabela
    ADD COLUMN nova_coluna VARCHAR(100);

    Finalmente, o comando DROP TABLE é utilizado para excluir uma tabela existente juntamente com todos os seus dados e objetos relacionados:

    Comandos SQL: removendo a tabela
    DROP TABLE minha_tabela;

    Antes de executar o comando DROP TABLE, verifique se você realmente deseja excluir a tabela e se não há dados importantes dentro dela. Essa operação é irreversível e não pode ser desfeita.

    INSERT

    O comando INSERT serve para adicionar novos registros a uma tabela existente. Ele permite inserir dados em uma ou mais colunas específicas de uma tabela, criando, assim, novas entradas de dados que podem ser consultadas, atualizadas ou excluídas posteriormente.

    Por exemplo, suponha que temos uma tabela chamada ‘usuários’ com colunas ‘id’, ‘nome’ e ‘e-mail’. Podemos inserir um novo usuário na tabela da seguinte maneira:

    Comandos SQL: inserindo dados na tabela
    INSERT INTO usuarios (nome, email)
    VALUES ('João', 'joao@example.com');

    Nesse exemplo, estamos inserindo um novo registro na tabela ‘usuários’, fornecendo valores para as colunas ‘nome’ e ‘e-mail’. Se a coluna ‘id’ for uma chave primária autoincrementável, ela será gerada automaticamente pelo sistema.

    Além disso, o comando INSERT também permite inserir múltiplas linhas de uma vez, fornecendo uma lista de valores para cada coluna. Vamos exemplificar isso inserindo dados nas nossas tabelas criadas anteriormente (replicando tudo no schema aplicacao2):

    Comandos SQL: inserindo dados em várias tabelas
    -- Inserção de dados na tabela1_app1
    INSERT INTO aplicacao1.tabela1_app1 (nome, idade) VALUES ('João', '30');
    INSERT INTO aplicacao1.tabela1_app1 (nome, idade) VALUES ('Maria', '25');
    INSERT INTO aplicacao1.tabela1_app1 (nome, idade) VALUES ('Pedro', '40');
    
    -- Inserção de dados na tabela2_app1
    INSERT INTO aplicacao1.tabela2_app1 (endereco) VALUES ('Rua A, 123');
    INSERT INTO aplicacao1.tabela2_app1 (endereco) VALUES ('Avenida B, 456');
    INSERT INTO aplicacao1.tabela2_app1 (endereco) VALUES ('Praça C, 789');
    
    -- Inserção de dados na tabela3_app1
    INSERT INTO aplicacao1.tabela3_app1 (email) VALUES ('joao@example.com');
    INSERT INTO aplicacao1.tabela3_app1 (email) VALUES ('maria@example.com');
    INSERT INTO aplicacao1.tabela3_app1 (email) VALUES ('pedro@example.com');

    SELECT

    O comando SELECT permite recuperar dados de uma ou mais tabelas de um banco de dados. Com o SELECT, é possível realizar consultas complexas para obter exatamente os dados desejados, filtrar resultados, realizar cálculos e até mesmo unir informações de várias fontes diferentes.

    A seguir, você pode conferir um exemplo simples de utilização do comando SELECT:

    Comandos SQL: consultando dados de uma tabela
    SELECT * FROM minha_tabela;

    Nesse exemplo, estamos selecionando todos os registros e todas as colunas da tabela minha_tabela. O * é um operador curinga que significa “todas as colunas”. Se desejar, você também pode categorizar colunas específicas para retornar:

    Comandos SQL: consultando colunas específicas de uma tabela
    SELECT nome, idade FROM minha_tabela;

    Além disso, o SELECT permite realizar filtragens e ordenações nos resultados, utilizando a cláusula WHERE para especificar condições de filtro e a cláusula ORDER BY para ordenar os resultados de acordo com uma ou mais colunas:

    Comandos SQL: consultando dados de uma tabela com um filtro
    SELECT * FROM minha_tabela WHERE idade > 18 ORDER BY nome;

    Com esse comando, estamos selecionando todos os registros da tabela minha_tabela, em que a idade é maior que 18, e ordenando os resultados pelo nome.

    O comando SELECT também suporta funções de agregação, como SUM, COUNT, AVG, entre outras, que podem ser usadas para realizar cálculos sobre os dados selecionados:

    Comandos SQL: contando dados de uma tabela
    SELECT COUNT(*) FROM minha_tabela;

    Esse comando retorna o número total de registros na tabela minha_tabela.

    UPDATE

    O comando UPDATE é utilizado no PostgreSQL para modificar os dados existentes em uma ou mais linhas de uma tabela. Ele oferece uma maneira eficiente de realizar alterações nos registros já existentes, permitindo atualizar valores de colunas de acordo com condições específicas. Confira a seguir um exemplo simples de utilização do comando UPDATE:

    Comandos SQL: atualizando dados de uma coluna
    UPDATE minha_tabela
    SET coluna1 = novo_valor
    WHERE condicao;

    Nesse exemplo, estamos atualizando os valores da coluna coluna1 na tabela minha_tabela, definindo-os como novo_valor, apenas para as linhas que atendem à condição especificada após a cláusula WHERE.

    Por exemplo, para aumentar o salário de todos os funcionários com mais de 10 anos de serviço, podemos fazer o seguinte:

    Comandos SQL: atualizando dados de uma coluna com filtro
    UPDATE funcionarios
    SET salario = salario * 1.1
    WHERE anos_de_servico > 10;

    Com esse comando, estamos multiplicando o salário de todos os funcionários com mais de 10 anos de serviço por 1.1, aumentando-o em 10%.

    Além disso, o UPDATE permite atualizar múltiplas colunas em uma única consulta e utilizar subconsultas para determinar os novos valores a serem atribuídos. Por exemplo, vamos supor que temos uma tabela chamada ‘tarefas’ com as colunas ‘id’, ‘descricao’ e ‘status’. Queremos atualizar todas as tarefas que estão com o status ‘Atrasada’ para o status ‘Em Andamento’:

    Comandos SQL: atualizando dados de várias colunas com filtro
    UPDATE tarefas
    SET status = 'Em Andamento'
    WHERE status = 'Atrasada';

    Com esse comando, estamos atualizando o status de todas as tarefas que estão como ‘Atrasada’ para ‘Em Andamento’.

    DELETE

    O comando DELETE é usado para remover registros de uma tabela. Ele permite remover uma ou mais linhas de uma tabela, de acordo com as condições especificadas, proporcionando uma maneira eficaz de limpar ou alterar os dados em um banco de dados. Exemplo:

    Comandos SQL: excluindo dados de uma tabela com filtro
    DELETE FROM minha_tabela
    WHERE condicao;

    Nesse exemplo, estamos removendo registros da tabela minha_tabela que atendem à condição especificada após a cláusula WHERE. Isso significa que apenas as linhas que atendem a essa condição serão excluídas da tabela. Por exemplo, para remover todas as tarefas que estão concluídas e foram marcadas como ‘finalizadas’, podemos fazer o seguinte:

    Comandos SQL: excluindo dados de uma tabela com filtro
    DELETE FROM tarefas
    WHERE status = 'finalizadas';

    Com esse comando, estamos excluindo todos os registros da tabela ‘tarefas’ que têm o status ‘finalizadas’.

    O comando DELETE também pode ser usado sem uma cláusula WHERE para excluir todos os registros de uma tabela:

    Comandos SQL: excluindo todos os dados de uma tabela
    DELETE FROM minha_tabela;

    Esse comando remove todos os registros da tabela minha_tabela, tornando-a vazia. Existe, ainda, uma outra forma mais rápida e eficiente de excluir todos os registros de uma tabela, utilizando o comando TRUNCATE.

    É importante ter cuidado ao usar o comando DELETE, pois ele remove os dados de forma permanente e irreversível. Certifique-se sempre de ter feito backup dos dados antes de executar operações de exclusão em larga escala ou com condições complexas.

    Conclusão

    Ok, agora você já viu os comandos básicos para criar seus objetos (com os comandos CREATE / ALTER / DROP), manipular seus dados (com os comandos INSERT, UPDATE e DELETE) e, por fim, como extrair os dados com o comando SELECT

    Em tese, isso é tudo o que você precisa para começar a colocar suas ideias em prática. Com o tempo, veremos que existem inúmeras opções e alternativas para cada um dos comandos apresentados aqui. Nós iremos revisitar cada um desses comandos com mais calma e atenção mais para a frente. No entanto, recomendamos que você não espere tanto assim. Tente, erre, comece novamente. Nada pode substituir a sua experiência pessoal por mais exemplos e descrições detalhadas que possamos dar aqui. Portanto, tire um tempo para testar, brincar e, se bobear, até se divertir. Só lembre-se de fazer isso longe dos bancos de dados de produção!

  • Utilização do psql

    Utilização do psql

    Introdução

    O psql é a ferramenta de linha de comando padrão para interagir com bancos de dados PostgreSQL, permitindo que você execute consultas SQL, configure o servidor, administre usuários e bancos de dados, importe e exporte dados, e muito mais. Neste artigo, você vai aprender sobre os principais utilitários, parâmetros e comandos, como também verá diversos exemplos de diferentes situações de uso.

    Roteiro:

    • Conexão
    • Variáveis internas do psql
    • Comandos básicos
    • Arquivo psqlrc
    • Arquivo psql_history
    • Importar e exportar dados

    Conexão

    Para abrir o executável do psql, podemos rodar apenas “psql” (supondo que o binário já esteja na variável de ambiente PATH) ou então passar o caminho completo do seu binário.
    Assim que entramos, é possível ver algumas informações sobre o PostgreSQL, como a versão do psql e do servidor, que pode ser diferente a depender da instalação:

    • Com cliente (psql) e servidor na mesma versão:

    Linux shell: acessando o psql
    $ psql
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    Type "help" for help.

    • Com cliente (psql) e servidor em versões diferentes, sendo a versão do psql a 15.7 e do servidor a 16.2:

    Linux shell: acessando o psql
    $ /usr/lib/postgresql/15/bin/psql
    psql (15.7 (Debian 15.7-1.pgdg120+1), server 16.2 (Debian 16.2-1.pgdg120+2))
    WARNING: psql major version 15, server major version 16.
             Some psql features might not work.
    Type "help" for help.
    
    postgres=#

    Ao acessar o psql, é possível passar alguns parâmetros, uns com informações da conexão, outros com ações para serem realizadas imediatamente após a conexão. Entretanto, mesmo com essa possibilidade, o psql é inteligente o suficiente para inferir alguns parâmetros de acesso automaticamente ou usar valores padrão, quando possível, e também variáveis de ambiente se estiverem configuradas.

    Alguns dos principais parâmetros de conexão são:

    • -h ou --host: host (endereço) onde o servidor PostgreSQL está sendo executado (o padrão é localhost);
    • -p ou --port: número da porta em que o servidor PostgreSQL está recebendo as conexões (o padrão é 5432);
    • -U ou --username: nome do usuário usado para autenticar a conexão com o servidor (o padrão é o nome do usuário atual do sistema operacional).

    E algumas das principais opções:

    • -d ou –-dbname: nome do banco de dados (o padrão é o nome do usuário passado);
    • -c ou --command: permite que você forneça uma consulta SQL ou um comando diretamente na linha de comando para ser executado imediatamente após a conexão ser estabelecida;
    • -v ou --variable: define uma variável de ambiente para a sessão do psql, que pode ser útil para personalizar o comportamento do psql durante uma sessão (veremos mais sobre isso adiante);
    • -l ou --list: lista todos os bancos de dados;
    • -E ou --echo-hidden: usado para exibir as consultas feitas pelos comandos internos do psql.

    Considerando os parâmetros citados, alguns exemplos de acesso ao psql via Linux são:

    • Acesso com parâmetros -h de endereço do servidor, -p de porta, -d de nome do banco de dados e -U de nome do usuário:

    Linux shell: acessando o psql com parâmetros
    $ psql -h localhost -p 5432 -d banco_exemplo -U usuario_exemplo
    Password for user usuario_exemplo:
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
    Type "help" for help.

    • Acesso com parâmetros -h de endereço do servidor (dessa vez utilizando um IP), porta como default (sem passar o parâmetro, portanto, acessando diretamente a 5432), parâmetro de usuário e banco:

    Linux shell: acessando o psql com parâmetros
    $ psql -h 64.23.248.23 -U usuario_ex -d banco_exemplo
    Password for user usuario_ex:
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
    Type "help" for help.
    
    banco_exemplo=>

    • Acesso sem parâmetro, que permite que o psql interprete o usuário e o nome do banco de dados automaticamente, considerando o mesmo nome do usuário atual do Linux (nesse caso, usuário “postgres”, que acessou o usuário de mesmo nome no servidor e também entrou no banco chamado “postgres”):

    Linux shell: acessando o psql sem nenhum parâmetro
    $ psql
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    Type "help" for help.
    
    postgres=#

    • Acesso com parâmetros de endereço do servidor e apenas do usuário, ocultando o nome do banco de dados e realizando a ação citada acima: caso seja omitido o nome do banco, o psql busca por algum banco que tenha o mesmo nome do usuário:

    Linux shell: acessando o psql com apenas alguns parâmetros
    $ psql -h 64.23.248.23 -U usuario_ex
    Password for user usuario_ex:
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
    Type "help" for help.
    
    usuario_ex=>

    • Acesso ao psql com parâmetro -c para utilização de um comando depois de acessá-lo (o acesso é feito, o comando rodado e depois você é desconectado do psql, então ele entra apenas para rodar o comando fornecido), no nosso caso, um SELECT dos dados de uma tabela:

    psql: acessando o psql com um comando SQL
    $ psql -c "SELECT * FROM minha_tabela;"
     id |      nome
    ----+----------------
      1 | Ana Clara
      2 | João Silva
      3 | Maria Souza
    (3 rows)
    
    $

    • Acesso com parâmetro -v, definindo uma variável chamada “meu_nome”, com o valor “João”, e depois utilizando dessa variável para uma consulta na tabela “minha_tabela”:

    psql: acessando o psql definindo uma variável
    $ psql -v meu_nome="'João'"
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    Type "help" for help.
    
    postgres=# SELECT * FROM minha_tabela WHERE nome = :meu_nome;
     id | nome
    ----+------
      4 | João
    (1 row)

    • Acesso com parâmetro -l listando todos os bancos existentes no servidor (os bancos postgres, template0 e template1 são criados automaticamente na instalação):

    Linux shell: acessando o psql com um comando interno
    $ psql -l
                                List of databases
    Name           |  Owner   | Encoding | Locale Provider | Collate |  Ctype  | ICU Locale | ICU Rules |   Access privileges
    ---------------+----------+----------+-----------------+---------+---------+------------+-----------+-----------------------
     banco_exemplo | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           |
     postgres      | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           |
     template0     | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           | =c/postgres          +
                   |          |          |                 |         |         |            |           | postgres=CTc/postgres
     template1     | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           | =c/postgres                 

    Os parâmetros para acessar o psql não são fornecidos aleatoriamente, existe uma ordem a ser seguida para que a conexão seja realizada com sucesso, pedindo as opções antes dos parâmetros de conexão. Essa ordem e outras informações úteis podem ser consultadas ao chamar o psql junto com o utilitário --help no shell:

    Linux shell: consultando a ajuda do pacote psql
    $ psql --help
    psql is the PostgreSQL interactive terminal.
    
    Usage:
      psql [OPTION]... [DBNAME [USERNAME]]

    Um exemplo de acesso pelo Windows seria assim:

    Windows shell: acessando o psql com parâmetros
    > & "C:\Program Files\PostgreSQL\16\bin\psql.exe" -U usuario_ex -d banco_ex
    Senha para o usuário usuario_ex:
    
    psql (16.2)
    Digite "help" para obter ajuda.
    
    banco_ex=#

    Arquivo .psqlrc

    O arquivo .psqlrc é um arquivo de configuração que possibilita personalizar o comportamento padrão do psql ao iniciá-lo, permitindo a execução automática de uma variedade de comandos SQL e configurações especiais sempre que você acessa o psql. Isso acontece porque o psql procura automaticamente por esse arquivo, geralmente localizado em %APPDATA%\postgresql no Windows e no diretório home do usuário no Linux (~/.psqlrc).

    Entre os comandos e as configurações que podem ser definidos no arquivo psqlrc, temos:

    • Definição de variáveis, as quais serão usadas durante sua sessão do psql;
    • Atalhos de comando: definição de atalhos de comando personalizados. Isso pode ser útil para criar abreviações para consultas SQL longas ou frequentemente usadas;
    • Configurações de exibição: personalização da aparência do psql, como alterar o formato de exibição das tabelas ou definir cores para destacar diferentes tipos de saída;
    • Comandos de inicialização: execução de comandos SQL específicos sempre que o psql é iniciado, ou seja, você pode definir um comando para exibir o tempo de execução das consultas SQL realizadas naquela sessão, com \timing.

    Veja um exemplo de configuração útil que recomendamos para inserir no arquivo:

    Text editor: visualizando o conteúdo do arquivo .psqlrc
    \pset null '(null)'
    \set PROMPT1 '%m: %n@%/%R%#%x '
    \set PROMPT2 '%m: %n@%/%R%#%x '
    \timing
    \x auto

    Explicando cada exemplo:

    • \pset null '(null)': define como o valor NULL será exibido no psql, sendo o valor padrão uma string vazia, alterado aqui para a string ‘(null)’, o que facilita a visualização dos dados;
    • \set PROMPT1 ‘%m: %n@%/%R%#%x ’ e \set PROMPT2 ‘%m: %n@%/%R%#%x ’:  define o prompt que será exibido no psql, sendo que a string ‘%m’ representa o endereço do servidor, a string ‘%n’ representa o nome do usuário na sessão, o @ serve apenas para separar uma informação da outra, seguido pela string ‘$/’ que mostra o nome da base de dados atual, depois temos a ‘/%R’ que mostra a informação sobre nossa conexão, sendo o valor padrão ‘=‘ e o valor que indica desconexão ‘!‘, a string ‘#’ mostra se estamos conectados com um superusuário ao exibir ‘#’ e ‘>’ no padrão, e a string ‘%x’ mostra o status de uma transação ‒ sendo ela vazia quando a transação acontece normalmente, ‘*’ para transações bloqueadas, ‘!’ para transações com falha ou ainda ‘?’ para transações com estado indeterminado. Você pode conferir diversas opções de personalização nesta página.
    • \set PROMPT2: diz respeito ao prompt que temos quando estamos finalizando algum comando SQL em outra linha, antes de terminá-lo com ‘;’ + Enter. Para manter a mesma visualização, é só repetir as mesmas configurações.

    Agora, veja um exemplo sem a configuração:

    psql shell: acesso sem configuração de prompt
    postgres=#

    E outro com a configuração:

    psql shell: acesso com configuração de prompt
    [local]: postgres@postgres=# 
    • \timing: ativa o cronômetro do psql, que mostrará o tempo decorrido para cada comando executado;
    • \x auto: ativa o modo de display expandido automático, para melhor exibir os resultados de consultas no terminal. Aqui, o psql tenta escolher automaticamente o modo que melhor se adapta ao tamanho do terminal, alternando entre modo expandido e não expandido.

    Linux

    Para criarmos o arquivo .psqlrc em sistemas Linux, podemos usar o seguinte comando:

    Linux shell: criando o arquivo .psqlrc
    nano ~/.psql

    Após criado e aberto o arquivo, basta inserir nele suas configurações desejadas, conforme exemplo acima.

    Windows

    O arquivo deve ser criado na pasta de dados de aplicações do usuário, sendo essa uma pasta oculta, que tem como uma forma de acesso o atalho “Windows + R” > digite “%APPDATA%” > clique no botão Enter. A diferença aqui é o nome do arquivo, de acordo com a documentação oficial, ele deve ser chamado de psqlrc.conf, no caminho C:\Users\<usuario>\AppData\Roaming\postgresql (se não houver a pasta “postgresql” ao abrir AppData via atalho, basta criá-la).
    O conteúdo é o mesmo do exemplo anterior.

    Arquivo psql_history

    O arquivo psql_history é um arquivo de texto que armazena o histórico de comandos que você executou no psql. Esse arquivo pode ser útil para:

    • Rever comandos: acessar comandos que você executou anteriormente, o que pode ser útil para depuração, repetição de comandos ou consulta de histórico;
    • Compartilhar comandos: compartilhar o histórico de comandos com outros usuários ou para fins de documentação;
    • Automatizar tarefas: usar scripts que reproduzem comandos do histórico para automatizar tarefas no psql.

    Em sistemas Linux, ele também está localizado no diretório home do usuário (~/.psql_history).

    Variáveis internas do psql

    No psql do PostgreSQL, além das variáveis definidas pelo usuário, como :meu_nome exemplificada anteriormente, existem também variáveis internas predefinidas durante a sessão, que podem ser úteis em diferentes cenários, por exemplo, para gerar relatórios e executar scripts. Para conferir todas as opções, basta acessar a página do site oficial aqui.
    Algumas das principais são:

    • :DBNAME: retorna o nome do banco de dados atualmente conectado;
    • :USER: retorna o nome do usuário atualmente conectado ao banco de dados;
    • :PORT: retorna o número da porta usada para a conexão com o servidor PostgreSQL;
    • :HOST: retorna o nome do host onde o servidor do banco de dados PostgreSQL está sendo executado.

    Para consultar o valor de alguma dessas variáveis, podemos usar o utilitário \echo:

    psql shell: consultando variáveis
    \echo :'DBNAME'
    \echo :'USER'
    \echo :'PORT'
    \echo :'HOST'
    
    'postgres'
    'postgres'
    '5432'
    '192.168.0.10'

    Comandos básicos

    O psql oferece uma variedade de comandos para interagir com o banco de dados PostgreSQL, que podem ser consultados no site oficial. Aqui estão alguns dos comandos básicos (todos utilizam o console interativo, então, para sair, é só apertar a letra q):

    • \?: exibe a lista de comandos disponíveis do psql, com explicação do que cada um faz, permitindo navegar por essa lista;
    • \h: exibe a lista de comandos SQL;
    • \h <instrução SQL>: usado para obter ajuda sobre uma instrução SQL específica. Deve-se digitar \h seguido pelo nome de uma instrução SQL (por exemplo, \h SELECT), e o psql exibe informações detalhadas sobre a sintaxe e o uso da instrução SQL especificada;
    • Comandos \d (é possível acrescentar “S” e/ou “+” em cada um deles, sendo “S” para mostrar objetos internos do sistema e “+” para revelar informações adicionais):
      • \d: lista todos os objetos do schema atual;
      • \d <nome do objeto>: mostra detalhes da estrutura do objeto especificado, podendo ser tabelas, views, índices, funções e sequências;
      • \dt: lista as tabelas no schema atual;
      • \df: lista todas as funções do schema atual; 
      • \di:  lista todos os índices existentes no schema atual;
      • \dx: lista todas as extensões instaladas no schema atual.
      • \dn: lista os schemas do banco;
      • \du: lista todos as roles do banco;
    • \l: lista os bancos de dados existentes;
    • \set: define uma variável de sessão com nome e valor específicos;
    • \unset: remove uma variável de sessão previamente definida com \set;
    • \i <nome do arquivo>: executa comandos SQL a partir de um arquivo, ou seja, importa arquivos no psql;
    • \o <nome do arquivo>: redireciona a saída de comandos para um arquivo de texto, ou seja, exporta arquivos no psql;
    • \o: termina o redirecionamento para o arquivo aberto anteriormente;
    • \t: ativa ou desativa a exibição do cabeçalho com os nomes das colunas e o rodapé com a contagem de linhas do resultado;
    • \x: ativa ou desativa a formatação de tabelas expandida, o que facilita a leitura e a visualização de tabelas com muitas colunas.

    E aqui estão exemplos de uso desses comandos:

    \l:

    psql shell: exemplo do comando \l
    # \l
                                                         List of databases
         Name      |  Owner   | Encoding | Locale Provider | Collate |  Ctype  | ICU Locale | ICU Rules |   Access privileges
    ---------------+----------+----------+-----------------+---------+---------+------------+-----------+-----------------------
     banco_exemplo | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           |
      postgres     | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           |
     template0     | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           | =c/postgres          +
                   |          |          |                 |         |         |            |           | postgres=CTc/postgres
     template1     | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           | =c/postgres          +
                   |          |          |                 |         |         |     
     (7 rows)

    \d:

    psql shell: exemplo do comando \d
    # \d
                         List of relations
       Schema   |         Name         |   Type   |    Owner
    ------------+----------------------+----------+-------------
     meu_schema | Exemplo              | table    | meu_usuario
     meu_schema | Exemplo_id_seq       | sequence | meu_usuario
     meu_schema | Outro_exemplo        | table    | meu_usuario
     meu_schema | Outro_exemplo_id_seq | sequence | meu_usuario
     meu_schema | exemplo2             | table    | meu_usuario
     meu_schema | exemplo2_id_seq      | sequence | meu_usuario
     meu_schema | outro_exemplo        | table    | meu_usuario
     meu_schema | outro_exemplo_id_seq | sequence | meu_usuario
    (8 rows)

    \dt:

    psql shell: exemplo do comando \dt
    postgres=# \dt
                 List of relations
     Schema |     Name      | Type  |  Owner
    --------+---------------+-------+----------
     public | alunos        | table | postgres
     public | cidades       | table | postgres
     public | coordenadores | table | postgres
     public | minha_tabela  | table | postgres
     public | nomes         | table | postgres
     public | pessoas       | table | postgres
     public | professores   | table | postgres
    (7 rows)

    \dt, considerando schemas diferentes do mesmo banco de dados (schemas meu_schema e public). Veja que, para pegar todas as informações do schema e não apenas uma tabela específica, podemos utilizar o caracter *:

    psql shell: exemplo do comando \dt com schemas especificados
    postgres=# \dt meu_schema.*
                  List of relations
       Schema   |   Name   | Type  |    Owner
    ------------+----------+-------+-------------
     meu_schema | exemplo2 | table | meu_usuario
    (1 row)
    
    postgres=# \dt public.*
               List of relations
     Schema |  Name   | Type  |    Owner
    --------+---------+-------+-------------
     public | pessoas | table | meu_usuario
    (1 row)

    \d <nome do objeto>:

    psql shell: exemplo do comando \d com objeto especificado
    postgres=# \d alunos;
                                        Table "public.alunos"
     Column |   Type            | Collation | Nullable |  Default
    --------+------------------------+-----------+----------+---------------------------------
     id     | integer           |           | not null | nextval('alunos_id_seq'::regclass)
     nome   | char varying(255) |           | not null |
    Indexes:
        "alunos_pkey" PRIMARY KEY, btree (id)

    \i <nome do arquivo>:

    Vamos começar criando um arquivo com um script para ser executado no psql:

    Debian shell: criando o arquivo meu_script.sql
    $ nano meu_script.sql
    
    INSERT INTO alunos (nome) VALUES ('João'), ('Caio'), ('Felipe');
    
    SELECT * FROM alunos;
    
    UPDATE alunos SET nome = 'Filipe' WHERE id = 6;
    
    SELECT * FROM alunos;

    Agora, vamos mostrar como rodar esse script com o \i:

    psql shell: exemplo de input do script criado (meu_script.sql)
    $ psql
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    Type "help" for help.
    
    postgres=# SELECT * FROM alunos;
     id |  nome
    ----+---------
      1 | Ludmila
      2 | Maria
      3 | Ana
    
    postgres=# \i meu_script.sql
    INSERT 0 3
     id |  nome
    ----+---------
      1 | Ludmila
      2 | Maria
      3 | Ana
      4 | João
      5 | Caio
      6 | Felipe
    (6 rows)
    
    UPDATE 1
     id |  nome
    ----+---------
      1 | Ludmila
      2 | Maria
      3 | Ana
      4 | João
      5 | Caio
      6 | Filipe
    (6 rows)

    No exemplo, é possível ver uma consulta de todos os dados da tabela alunos antes e depois do script rodar, adicionando três novos nomes e atualizando um deles (de “Felipe” para “Filipe”).

    • \o (deve ser usado antes do início do conteúdo que se deseja direcionar para o arquivo e, depois, para informar ao psql quando parar de enviar a saída) e \t (para um conteúdo mais limpo no segundo arquivo gerado):
    psql shell: exemplo de output de saída
    postgres=# \o alunos_nomes.txt
    postgres=# SELECT * FROM alunos;
    postgres=# \o
    postgres=# \o alunos_nomes_t.txt
    postgres=# \t
    Tuples only is on.
    postgres=# SELECT * FROM alunos;
    postgres=# \o
    postgres=# \q
    
    $ ls
    alunos_nomes.txt     alunos_nomes_t.txt      meu_script.sql     ...
    
    $ cat alunos_nomes.txt
     id |  nome
    ----+---------
      1 | Ludmila
      2 | Maria
      3 | Ana
      4 | João
      5 | Caio
      6 | Filipe
    (6 rows)
    
    $ cat alunos_nomes_t.txt
      1 | Ludmila
      2 | Maria
      3 | Ana
      4 | João
      5 | Caio
      6 | Filipe

    No exemplo, é possível ver o arquivo resultante da saída, chamado alunos_nomes.txt, com os títulos da coluna e informando o total de linhas e o alunos_nomes_t.txt, que foi rodado com o \t antes de finalizar a saída, trazendo um resultado mais limpo.

    • \x (exemplos da saída de uma consulta na tabela pg_class, o primeiro com o \x desativado, e o segundo com ele ativado):
    psql shell: exemplo de consulta com o \x desativado
    postgres=# SELECT * FROM pg_class;
    
      oid  |                    relname                     | relnamespace | reltype | reloftype | relowner | relam | relfilenode | reltablespace | relpages | reltuples | relallvisible | reltoastrelid | relhasindex | relisshared | relpersistence | relkind | relnatts | relchecks | relhasrules | relhastriggers | relhassubclass | relrowsecurity | relforcerowsecurity | relispopulated | relreplident | relispartition | relrewrite | relfrozenxid | relminmxid |                          relacl                          |       reloptions        | relpartbound
    -------+------------------------------------------------+--------------+---------+-----------+----------+-------+-------------+---------------+----------+-----------+---------------+---------------+-------------+-------------+----------------+---------+----------+-----------+-------------+----------------+----------------+----------------+---------------------+----------------+--------------+----------------+------------+--------------+------------+----------------------------------------------------------+-------------------------+--------------
     16399 | minha_tabela_id_seq                            |         2200 |       0 |         0 |       10 |     0 |       16399 |             0 |        1 |         1 |             0 |             0 | f           | f           | p              | S       |        3 |         0 | f           | f              | f              | f              | f                   | t              | n            | f              |          0 |            0 |          0 |           
    ...   
    psql shell: exemplo de consulta com o \x ativado
    postgres=# \x
    postgres=# SELECT * FROM pg_class;
    
    -[ RECORD 1 ]-------+---------------------------------------------------------
    oid                 | 16399
    relname             | minha_tabela_id_seq
    relnamespace        | 2200
    reltype             | 0
    reloftype           | 0
    relowner            | 10
    relam               | 0
    relfilenode         | 16399
    reltablespace       | 0
    relpages            | 1
    reltuples           | 1
    relallvisible       | 0
    reltoastrelid       | 0
    relhasindex         | f
    relisshared         | f
    relpersistence      | p
    relkind             | S
    relnatts            | 3
    relchecks           | 0
    relhasrules         | f
    relhastriggers      | f
    relhassubclass      | f
    relrowsecurity      | f
    relforcerowsecurity | f
    relispopulated      | t
    relreplident        | n
    relispartition      | f
    relrewrite          | 0
    relfrozenxid        | 0
    relminmxid          | 0
    relacl              |
    reloptions          |
    relpartbound        |
    ...

    Conclusão

    O psql é uma ferramenta com muitas opções e facilidades. Aqui, vimos apenas uma amostra de todo o seu potencial. Ele é também uma ferramenta nativa, que vem distribuída junto com os pacotes originais do PostgreSQL. Isso significa que ele é mantido e atualizado com o mesmo rigor e cuidado que o servidor, o que o torna extremamente confiável e robusto. 
    No entanto, usar o psql para escrever longas e complexas consultas pode ser desagradável, mas você pode utilizar o seu editor de textos favoritos para escrever seu código SQL e executá-lo no psql com a opção \i, porém, muitas vezes, parece mais cômodo utilizar uma ferramenta gráfica para isso. Sem preconceitos, se você pesquisar, vai descobrir que 10 entre 10 DBAs Postgres preferem utilizar o psql no dia a dia. Entre as qualidades que esses profissionais apontam na hora de usar o psql, podemos mencionar:

    • Confiabilidade: o psql é desenvolvido e atualizado pela mesma equipe que desenvolve o PostgreSQL;
    • Sempre atualizado: toda nova funcionalidade do PostgreSQL implica atualizações correspondentes no psql para lidar com elas;
    • Robustez: como toda ferramenta em modo texto, a chance de ela quebrar quando rodar scripts SQL longos e pesados é muito baixa se comparada com qualquer outra ferramenta gráfica;
    • Está sempre disponível em qualquer ambiente: muitas vezes, você não tem acesso à porta 5432 ou não pode se conectar remotamente a um banco de dados de produção com uma ferramenta gráfica a partir do seu desktop. No entanto, todo servidor PostgreSQL tem por padrão um psql instalado junto com ele;
    • Facilidade de automatizar processos: o psql possui uma série de ferramentas para quem quer automatizar processos, criar jobs e outras tarefas do dia a dia de um DBA;
    • Simplicidade e eficiência: depois que você vence a curva de aprendizado inicial do psql, você deixa de utilizar o mouse e começa a ser bastante produtivo nas suas tarefas do dia a dia.

    Então, mesmo que você não seja um entusiasta de ferramentas em modo texto, é fundamental aprender a se virar minimamente com o psql, assim como com um editor de texto (como o nano ou o vi) no modo texto. Em situações de emergência ou quando precisar executar tarefas complexas, o psql será um aliado de primeira linha.

  • Gerenciando conexões com o arquivo pg_service

    Gerenciando conexões com o arquivo pg_service

    Introdução

    O arquivo pg_service.conf oferece uma maneira conveniente de definir informações de conexão para o PostgreSQL, permitindo acesso a diferentes instâncias do banco de dados sem a necessidade de especificar manualmente os detalhes de conexão toda vez. Essa ferramenta é especialmente útil em ambientes onde você precisa administrar várias instâncias do PostgreSQL ou quando você deseja ocultar informações de conexão em scripts ou aplicativos. A documentação completa desse recurso pode ser encontrada no site oficial do PostgreSQL.

    Roteiro:

    • O que é o pg_service.conf?
    • Localização do arquivo pg_service.conf
    • Configuração e exemplo

    O que é o pg_service.conf?

    O pg_service.conf é um arquivo de configuração usado pelo PostgreSQL para definir serviços e suas informações de conexão correspondentes. Cada serviço é identificado por um nome e contém detalhes como endereço, porta, banco de dados, usuário e outras opções de conexão (a lista completa de palavras-chave disponíveis está na documentação da libpq). 
    A documentação oficial pode ser conferida no site do PostgreSQL, nesta página.

    Localização do pg_service.conf

    Este arquivo pode ser encontrado no diretório home do usuário (assim como no arquivo pgpass.conf) e é utilizado pelo cliente psql para determinar as configurações de conexão.  Você pode verificar qual é o diretório home olhando o arquivo `/etc/passwd`, conferindo o conteúdo da variável de ambiente `$HOME` ou simplesmente criando o arquivo com o atalho ~/. 
    Ao centralizar todas as configurações em um único arquivo, facilita-se a manutenção e o gerenciamento das conexões, especialmente em cenários complexos com vários bancos de dados e servidores para se conectar. Além disso, há duas variáveis de ambiente relevantes relacionadas ao pg_service.conf, chamadas $PGSERVICEFILE e $PGSERVICE:

    • $PGSERVICEFILE: esta variável é utilizada para especificar o caminho para o arquivo pg_service.conf, permitindo que você indique um arquivo específico que seja o padrão. Isso é útil quando você deseja aumentar um pouco a segurança do arquivo ou compartilhá-lo com outros usuários no mesmo servidor;
    • $PGSERVICE: é usada para especificar o nome do serviço de conexão desejado. Quando definida, o psql tentará conectar-se ao serviço correspondente ao nome especificado no seu arquivo pg_service.conf. Trata-se apenas de um atalho para entrar por padrão em um serviço específico que você utiliza com mais frequência, sem precisar citar o nome dele.

    Se você tiver um servidor PostgreSQL rodando na máquina, você pode utilizar um .pg_service.conf compartilhado com vários usuários. Logo, ele ficará armazenado em um local diferente do diretório home do seu usuário. O local varia de acordo com a instalação do PostgreSQL, podendo ser verificado das seguintes formas:

    No Debian:

    Debian shell: verificando local do arquivo pg_service.conf
    $ pg_config --sysconfdir
    /etc/postgresql-common

    No RedHat:

    RedHat shell: verificando local do arquivo pg_service.conf
    $ /usr/pgsql-14/bin/pg_config --sysconfdir
    /etc/sysconfig/pgsql

    No Windows:

    Windows shell: verificando local do arquivo pg_service.conf
    & 'C:\Program Files\PostgreSQL\16\bin\pg_config.exe' --sysconfdir
    C:/Program Files/PostgreSQL/16/etc

    Configuração e exemplo

    Linux Debian

    Crie o arquivo pg_service.conf em um editor de texto no diretório inicial do usuário (o ponto no início demonstra se tratar de um arquivo oculto, o que aprimora sua segurança). Como utilizamos o editor nano, ficou assim (rodando com usuário postgres do sistema):

    Linux shell: criando o arquivo pg_service.conf
    $ nano ~/.pg_service.conf

    Dentro desse arquivo, você deve adicionar as seguintes informações, referentes ao nome que deseja dar ao serviço: o endereço do hospedeiro do servidor, o número de porta para conexão, o usuário que vai acessar o banco e o nome do banco:

    Linux shell: slavando informações do serviço no arquivo pg_service.conf
    [meu-servico]
    host=localhost
    port=5432
    user=usuario_ex1
    dbname=meu_banco

    É ainda possível ter mais de um serviço definido aqui, separando um do outro por uma linha pulada e que deve se iniciar com o nome do outro serviço entre colchetes novamente, conforme exemplo:

    Linux shell: adicionando mais um serviço no arquivo pg_service.conf
    [meu-servico]
    host=localhost
    port=5432
    user=usuario_ex1
    dbname=meu_banco
    
    [meu-outro-servico]
    host=127.0.0.1
    port=5433
    user-usuario_ex2
    dbname=meu_outro_banco
    password=minha_senha

    Após configurar o arquivo, ao conectar-se ao PostgreSQL, você pode usar os serviços definidos usando o utilitário psql. Por exemplo:

    Linux shell: utilizando o arquivo pg_service.conf para conexão ao psql
    psql service=meu-servico

    Isso fará com que o psql utilize as informações de conexão do serviço meu-servico definido no arquivo pg_service.conf. Se a senha não estiver especificada no arquivo de serviço, o psql solicitará a senha durante a conexão. Você pode ainda utilizar o pg_service.conf para armazenar a maioria das informações da conexão e deixar apenas as senhas em outro arquivo, o .pgpass, se quiser aumentar um pouco a segurança do ambiente.
    No entanto, se você for armazenar as senhas dos usuário no pg_service.conf, recomendamos mudar as permissões de acesso do arquivo para 600, como fazemos no pgpass:

    Linux shell: definindo permissões do arquivo pg_service.conf
    $ chmod 600 ~/.pg_service.conf
    $ ls -lha ~/.pg_service.conf
    -rw-r--r--. 1 postgres postgres 181 Apr  5 11:25 .pg_service.conf

    Windows

    Aqui, vamos criar o arquivo com o mesmo nome .pg_service.conf, porém com um local específico: na pasta de dados de aplicações do usuário, que é oculta, mas pode ser encontrada com o atalho “Windows + R” > digite “%APPDATA%” > clique no botão Enter. Dentro dessa pasta, deve-se procurar por uma chamada “postgresql”, e caso ela não exista, é só criar, pois esse será o local onde vamos guardar nosso arquivo: C:\Users\<usuário>\AppData\Roaming\postgresql
    Iremos inserir os mesmos dados que inserimos para o arquivo no Debian:

    Edição do arquivo .pg_service.conf no Windows

    Então, é só acessar o psql da mesma forma que a demonstrada para Debian:

    Windows shell: acessando o psql com o serviço do .pg_service.conf
    psql service=meu-servico

    Conclusão

    O pg_service é provavelmente a forma mais prática de gerenciar várias conexões com o PostgreSQL no modo texto. Gostaríamos, no entanto, de fazer aqui algumas considerações bem objetivas:

    • Se você utiliza apenas ferramentas gráficas, isso pode não fazer muito sentido para você. Ferramentas, como o pgAdmin4, têm seu próprio mecanismo para gerenciar conexões de forma gráfica;
    • Se você precisa que a sua aplicação acesse diferentes ambientes e necessita apenas guardar a senha de acesso fora da sua aplicação, o .pgpass pode ser uma solução simples e elegante para isso;
    • Se você precisa administrar apenas um ou dois servidores Postgres em um ambiente isolado, as variáveis de ambiente podem ser mais simples de se utilizar, além de serem uma solução mais direta e objetiva;
    • Se você, como nós, tem que lidar com diversos servidores, acessando diretamente via linha de comando, o pg_service.conf vai certamente facilitar a sua vida.

    Particularmente, recomendamos que você utilize uma combinação com as três soluções: 

    • Organize os dados das suas conexões com o .pg_service.conf, mas sem guardar as senhas nele;
    • Armazene as suas senhas no .pgpass, é uma forma elegante de ter um pouquinho mais de segurança ao lidar com senhas;
    • Utilize as variáveis de ambiente, como o $PGSERVICE, para configurar no seu shell o serviço padrão que você mais utiliza no dia a dia e agilize um pouco mais o seu trabalho.

    Claro, se você compartilha o mesmo ambiente com outras pessoas, convém combinar e documentar tudo isso para ninguém ficar confuso no meio de tantos recursos. 
    Na verdade, gostamos muito de utilizar usuários isolados (tanto no SO como no banco) em cada ambiente que acessamos, para garantir a privacidade, a segurança, a rastreabilidade e, claro, a conveniência para configurar o ambiente de trabalho de forma ágil, eficiente e personalizada. 
    Achamos que com esse conjunto de ferramentas as coisas vão começar a ficar mais fáceis. Só tome cuidado para não se perder no meio disso tudo. 

    Boa diversão!

  • Gerenciando senhas no PostgreSQL com o arquivo pgpass

    Gerenciando senhas no PostgreSQL com o arquivo pgpass

    Introdução

    Uma vantagem significativa de usar o arquivo pgpass é a conveniência que ele oferece ao automatizar ou simplificar conexões frequentes ao PostgreSQL, possibilitando a ligação com um banco de dados sem a necessidade de digitar a senha manualmente todas as vezes. Também é uma forma conveniente de autenticar aplicações, que usam a libpq para se conectarem no PostgreSQL, sem precisar armazenar as senhas dentro da aplicação. A documentação sobre esse recurso pode ser encontrada no site oficial do PostgreSQL, nesta página.

    Roteiro:

    • O que é o pgpass?
    • Configuração e exemplo

    O que é o pgpass?

    O arquivo pgpass é um arquivo de texto simples que contém uma ou mais linhas, cada uma representando uma entrada de autenticação para o PostgreSQL. 

    No arquivo pgpass, deve conter as seguintes informações: 

    • Endereço do servidor PostgreSQL (host) 
    • Número da porta de conexão 
    • Usuário do banco  
    • Senha 

    Nas três primeiras colunas (endereço, porta e usuário), você pode utilizar um * como coringa para simbolizar que aquela linha se aplica para qualquer valor daquele campo.
    Sem a utilização de espaços, essas informações devem ser separadas apenas por  “:”, conforme exemplo abaixo:

    Conteúdo do arquivo .pgpass
    hostname:port:database:username:password

    Esse arquivo também está relacionado com a variável de ambiente PGPASSFILE, que especifica o caminho para o arquivo pgpass. Quando o arquivo “pgpassfile” existir no local padrão, ou no local apontado pela variável de ambiente $PGPASSFILE, o PostgreSQL utilizará esse arquivo como fonte de informações de autenticação, em vez de solicitar explicitamente as credenciais de acesso.

    Configuração e exemplo

    Para configurarmos o pgpass, primeiramente será necessário ter em mãos as informações citadas anteriormente para inserirmos no arquivo, como o endereço do servidor PostgreSQL que se deseja acessar, o número da porta para conexão, o usuário que será utilizado e sua senha. Além disso, como citado, tanto para configuração em Linux quanto para Windows, é recomendável que seja criado o arquivo no diretório do usuário, para maior segurança, através do controle de permissões e da exclusividade de acesso do usuário em questão.
    Também é importante ter em mente que essa é uma alternativa às variáveis de ambiente, sendo considerada mais segura (as variáveis podem ser acessadas por qualquer processo em execução no sistema, o arquivo não) e que não deve ser utilizada em conjunto com elas, pois a chance de dar conflito no acesso ao psql é grande.

    Linux / Unix

    Os passos são bem simples: criar o arquivo com o nome .pgpass e inserir nele as informações citadas anteriormente.
    O arquivo deve ficar no diretório pessoal do usuário (mais conhecido como “diretório home”). Você pode verificar qual é o diretório home olhando o arquivo /etc/passwd, verificando o conteúdo da variável de ambiente $HOME ou simplesmente criando o arquivo com o atalho ~/. Você  deve sempre começar o nome do arquivo ‘pgpass‘ com um ponto, isso faz com que ele fique oculto em sistemas Linux/Unix, aumentando um pouco (mas não muito) a segurança do arquivo. Para criar/editar o arquivo, optamos aqui por utilizar o editor de texto nano, que já vai criar e abrir o arquivo para edição de uma só vez: 

    Debian shell: criando o arquivo .pgpass
    nano ~/.pgpass

    Com o arquivo aberto, vamos inserir as informações conforme o exemplo anterior. A seguir, é possível conferir como o nosso exemplo ficou (com uso de “localhost” por ser um banco local, para teste. Para acesso remoto, seria necessário fornecer o IP do servidor):

    Debian shell: conteúdo do arquivo .pgpass
    localhost:5432:novo_banco:usuario_ex:senha

    Após criado, o arquivo precisa ter as seguintes permissões 600, o que significa que apenas o proprietário tem permissão de leitura e escrita, e nenhum outro usuário tem alguma permissão, aumentando, assim, o nível de segurança. Para que isso seja garantido, podemos rodar o seguinte comando:

    Debian shell: alterando permissões do arquivo .pgpass
    $ chmod 600 ~/.pgpass
    $ ls -lha ~/.pgpass
    -rw-------. 1 postgres postgres 43 Apr  5 10:46 .pgpass

    Agora, para usarmos o arquivo para acessar o psql sem a necessidade de fornecer senha, podemos fazer da seguinte forma:

    Debian shell: acessando o psql com as informações do arquivo .pgpass
    $ psql -h localhost -p 5432 -d novo_banco -U usuario_ex
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
    Type "help" for help.
    
    novo_banco=>

    É possível observar o login automático sem a solicitação de senha do usuário em questão. 

    Também é permitido ter diversos usuários e senhas no .pgpass, sendo necessário apenas adicionar uma entrada em cada linha. Desse modo, quando tentar se conectar ao PostgreSQL, o utilitário psql irá verificar todas as entradas no arquivo até encontrar uma que corresponda às informações de conexão fornecidas (parâmetro -h para informar o endereço, -p da porta, -d do banco e -U do usuário). Com vários usuários, o arquivo .pgpass ficaria conforme exemplo abaixo:

    Debian shell: conteúdo do arquivo .pgpass
    localhost:5432:banco1:usuario1:senha1
    192.0.2.1:5432:banco2:usuario2:senha2
    172.16.0.1:5432:banco3:usuario3:senha3

    Windows 11

    Em sistemas Windows, podemos começar abrindo o Bloco de Notas e criando esse arquivo, que deve ser salvo na pasta de dados de aplicações do usuário, sendo essa uma pasta oculta, que tem como uma forma de acesso o seguinte atalho: “Windows + R” > digite “%APPDATA%” > clique no botão Enter. A diferença aqui é o nome do arquivo. De acordo com a documentação oficial, ele deve ser chamado de pgpass.conf, no caminho C:\Users\<usuario>\AppData\Roaming\postgresql (se não houver a pasta “postgresql” ao abrir AppData via atalho, basta criá-la).
    Criamos nosso arquivo e ele ficou assim:

    Para restringir as permissões desse arquivo, devemos acessar o Explorador de Arquivos e clicar com o botão direito no pgpass.conf para, depois, selecionar “Propriedades” e a aba “Segurança”. Lá é possível editar as permissões para outros usuários e também para grupos.

    Agora, já podemos fazer o acesso ao psql usando a senha do nosso arquivo, da mesma forma demonstrada para Debian:

    Windows shell: acessando o psql com as informações do arquivo pgpass.conf
    > psql -h localhost -p 5432 -d banco_ex -U usuario_ex
    
    psql (16.2)
    Digite "help" para obter ajuda.

    Conclusão

    O pgpass.conf é uma maneira relativamente segura de proteger suas senhas. Claro, se qualquer pessoa tiver acesso local à máquina, acesso com usuário privilegiado (como root no Linux/Unix ou Administrator no Windows) ou acesso ao usuário em questão, ela também vai conseguir de alguma forma visualizar esse arquivo e descobrir quais são as suas senhas. Você pode utilizar a variável de ambiente $PGPASSFILE e colocar o arquivo num local diferente do padrão para dificultar um pouco mais a vida de uma pessoa mal-intencionada, mas isso não vai impedi-la de qualquer modo. 
    O pgpass.conf será uma opção segura se, e apenas se, o controle de acesso ao ambiente onde ele está localizado for seguro também. Recomendamos fortemente que isso aconteça, particularmente se você for armazenar senhas de ambientes de produção ou de ambientes de homologação com dados de produção. Se isso não for possível, é melhor pensar em ferramentas externas para proteger as senhas de acesso ao PostgreSQL.

  • Variáveis de ambiente no PostgreSQL

    Variáveis de ambiente no PostgreSQL

    Introdução

    Lidar com conexões em vários bancos de dados pode ser um trabalho bastante chato e propenso a erros. Se você está buscando entender melhor como configurar e controlar o comportamento do seu ambiente de banco de dados PostgreSQL, é importante conhecer as variáveis de ambiente, que desempenham um papel fundamental. Essas variáveis oferecem uma forma flexível e poderosa de personalizar diversos aspectos do PostgreSQL, desde configurações de conexão até opções de segurança e localização de arquivos. 

    Neste guia, vamos explorar o conceito de variáveis de ambiente no contexto do PostgreSQL, discutindo como elas são utilizadas e quais são algumas das mais comuns. Como material de referência, utilizaremos a documentação oficial do PostgreSQL.

    Roteiro:

    • O que são variáveis de ambiente no PostgreSQL?
    • Configuração e exemplo
    • Aplicações que utilizam variáveis de ambiente
    • Segurança

    O que são variáveis de ambiente no PostgreSQL?

    Variáveis de ambiente podem ser usadas para controlar o comportamento do servidor e de outras ferramentas relacionadas ao PostgreSQL, como o cliente psql e outros utilitários. Elas são usadas para configurar aspectos como diretórios de dados, níveis de registro, conexões de rede, localização de bibliotecas, entre outros.

    Configuração e exemplo

    O primeiro passo é entender qual variável de ambiente você deseja configurar e qual é o seu propósito. Existem várias delas, como variável para guardar informação de usuário, do nome do banco, de porta para conexão etc. É possível conferir uma lista com as variáveis disponíveis no site oficial do PostgreSQL, nesta página.

    Entre as principais, estão:

    • PGHOST: especifica o host onde o servidor PostgreSQL está em execução, seja pelo nome dele ou por endereço de IP. Se não especificado, o PostgreSQL tentará se conectar ao servidor local;
    • PGPORT: define o número da porta que o servidor PostgreSQL usará para aceitar conexões TCP/IP. O valor padrão é 5432;
    • PGUSER: especifica o nome do usuário do PostgreSQL ao se conectar. Se não especificado, o nome de usuário do sistema operacional atual é usado;
    • PGDATABASE: especifica o nome do banco de dados ao qual se conectar. Se não especificado, o nome do usuário é usado como padrão;
    • PGPASSWORD: define a senha do usuário especificado pela variável PGUSER. Usado para autenticação automática ao conectar-se ao PostgreSQL.

    Linux (bash)

    Para exemplificar, vamos começar com as variáveis PGUSER e PGPASSWORD, e o primeiro passo é inserir seu dado de usuário e sua senha nas variáveis usando o comando export, desta forma (estamos no usuário “postgres”):

    export PGUSER=<usuario>
    export PGPASSWORD=<senha>

    O nosso, com um usuário de teste chamado “usuario_ex1”, e sua senha sendo “senha”, ficou assim: 

    Debian shell: definindo variáveis de ambiente
    $ export PGUSER=usuario_ex1
    $ export PGPASSWORD=senha

    Então, ao conectar com o psql, não é necessário passar o parâmetro de identificação do usuário e nem o da senha, que serão puxados automaticamente dessas variáveis, conforme vemos abaixo na consulta do usuário atual:

    Debian shell: acessando o psql sem parâmetro de usuário e senha
    $ psql -h localhost -d postgres
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
    Type "help" for help.
    
    postgres=> SELECT current_user;
     current_user
    --------------
     usuario_ex1
    (1 row)

    No exemplo anterior, foi rodado o comando SELECT current_user; dentro do psql para exibir o usuário atual, e podemos ver que a conexão com o usuário que definimos nas variáveis de ambiente foi realizada com sucesso.

    Agora, vamos definir também as variáveis PGHOST, PGPORT e PGDATABASE, buscando acessar o psql sem a necessidade de passar algum parâmetro.
    Seguindo os mesmo passos, vamos começar com o export para acessar e definir valor a essas variáveis:

    export PGHOST=<host>
    export PGPORT=<porta>
    export PGDATABASE=<database>

    Aqui ficou assim:

    Debian shell: definindo variáveis de ambiente
    export PGHOST=localhost
    export PGPORT=5432
    export PGDATABASE=meu_banco

    Portanto, ao acessar o psql sem nenhum parâmetro, este é o resultado:

    Debian shell: acessando o psql sem nenhum parâmetro
    $ psql
    psql (16.2 (Debian 16.2-1.pgdg120+2))
    SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
    Type "help" for help.
    
    meu_banco=> SELECT current_user;
     current_user
    --------------
     usuario_ex1
    (1 row)

    Busca e reset

    Para pesquisarmos as variáveis já configuradas e seus valores, o que é útil para o gerenciamento delas, basta rodar o seguinte comando:

    Debian shell: buscando variáveis de ambiente com o comando env
    $ env | grep PG
    PGPORT=5432
    PGPASSWORD=senha
    PGUSER=usuario_ex1
    PGDATABASE=meu_banco
    PGHOST=localhost

    Também podemos verificar as variáveis já configuradas e seus valores pelo próprio comando echo:

    Debian shell: buscando variável de ambiente com o comando echo
    $ echo $PGUSER
    usuario_ex1

    E se for necessário resetar alguma variável, é só rodar o mesmo comando de definição, com export, e deixar sem valor atribuído (ou trocar o valor, se precisar):

    export PGHOST=

    Windows 11

    Para definirmos nossas variáveis de ambiente no Windows 11, o primeiro passo é acessar o menu de variáveis de ambiente, que pode ser facilmente encontrado buscando por “variáveis” no Pesquisar do Windows e selecionando “Editar as variáveis de ambiente”. A primeira janela a ser aberta será esta:

    Propriedades do Sistema Windows 11

    Depois, será necessário acessar o menu citado clicando no botão “Variáveis de Ambiente…” para ver esta tela:

    Variáveis de Ambiente Windows 11

    E então é só adicionar as variáveis em “Variáveis do sistema” pelo botão “Novo” e inserir os valores desejados, conforme imagem abaixo:

    Criando variável de ambiente no Windows 11

    Aqui, configuramos as mesmas variáveis de antes: PGUSER, PGPASSWORD, PGDATABASE, e, além disso, também incluímos o caminho do executável do psql na variável de sistema “Path”, facilitando, assim, o acesso via cmd, sem a necessidade de passar o caminho do psql completo. O caminho que deve ser adicionado na variável “Path”, caso tenha optado por instalar no diretório padrão, é C:\Program Files\PostgreSQL\16\bin\, ficando desta forma:

    Editando a variável de ambiente Windows 11

    Após alterar as variáveis, pode ser necessário reiniciar o sistema. 

    Então, o acesso via cmd pode ser feito simplesmente rodando o comando psql:

    Windows shell: acessando o psql sem nenhum parâmetro
    PS C:\Users\ghans> psql
    psql (16.2)
    ADVERTÊNCIA: A página de código da console (850) difere da página de código do Windows (1252)
                 os caracteres de 8 bits podem não funcionar corretamente. Veja a página de
                 referência do psql "Notes for Windows users" para obter detalhes.
    Digite "help" para obter ajuda.
    
    banco_ex=#

    No mesmo assistente de configuração que acessamos para criar as variáveis, também podemos consultar as existentes e excluir ou alterar alguma delas quando necessário.

    Aplicações que utilizam variáveis de ambiente

    Temos uma série de aplicações clientes que utilizam as variáveis de ambiente se não tiverem parâmetros explícitos na linha de comando. É possível conferir essa lista dentro do site oficial do PostgreSQL clicando aqui. E caso queira entender como alguma aplicação específica lida com as variáveis, basta clicar no nome dela e ver quais e como elas são usadas. É bom ter em mente que qualquer aplicação que use a biblioteca libpq também utiliza as variáveis de ambiente.

    Segurança

    Entenda que variáveis de ambiente não são a forma mais segura de lidar com senhas de usuários. É muito fácil verificar o valor das variáveis correntes e pegar o conteúdo de uma variável PGPASSWORD, por exemplo. Existem opções um pouco mais seguras para lidar com isso, como o uso do “.pgpass”, como veremos em breve. Portanto, use as variáveis de ambiente com parcimônia, principalmente a variável PGPASSWORD.

    Conclusão

    Variáveis de ambiente tornam a vida mais simples, uma vez que não precisamos repetir a mesma coisa várias vezes. No entanto, ao entrar, pela primeira vez, sem perceber, num local que está utilizando variáveis de ambiente, isso pode levar você a comportamentos inesperados. Você pode esperar que o valor padrão seja utilizado, e o comportamento das coisas vai parecer muito estranho de repente. Ou seja, ame ou odeie, é importante aprender a lidar com as variáveis de ambiente e entender o seu comportamento no dia a dia. 

    Veremos, nos artigos seguintes, que existem outras opções mais organizadas para lidar com locais complexos, onde você precisa se conectar em vários ambientes diferentes, como o uso do .pg_service.conf.

plugins premium WordPress