Catálogo do sistema (pg_catalog)
9 de Setembro de 2024, 16:27 - sem comentários aindaIntroduçã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):
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:
[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:
[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:
[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.
[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.
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:
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:
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:
$ 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:
# 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:
$ 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:
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:
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/:
# 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:
# 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:
# 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:
[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:
[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:
# 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:
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:
# 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:
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:
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:
localhost: dba@aplicacao=# TRUNCATE TABLE aplicacao1.tabela1_app1;
TRUNCATE TABLE
Nova consulta dos dados da tabela na pg_class, após o 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:
$ 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
2 de Setembro de 2024, 18:11 - sem comentários aindaIntroduçã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 -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.
# 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:
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;
- 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
-
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:
# 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:
# 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
29 de Agosto de 2024, 16:16 - sem comentários aindaIntroduçã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):
\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:
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:
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:
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:
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:
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):
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
dba@aplicacao=# GRANT UPDATE ON ALL TABLES IN SCHEMA aplicacao1 TO editores;
Agora, podemos adicionar o usuário app1_user2 nessa 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):
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:
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:
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:
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 -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
:
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:
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:
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:
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:
dba@aplicacao=# GRANT USAGE ON SCHEMA aplicacao1 TO app1_user3;
dba@aplicacao=# GRANT USAGE ON SCHEMA aplicacao1 TO app1_user4;
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:
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:
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:
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:
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:
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:
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:
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:
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
20 de Agosto de 2024, 17:50 - sem comentários aindaIntroduçã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:
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:
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:
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:
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:
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:
ALTER DATABASE meu_banco OWNER TO novo_proprietario;
E o DROP DATABASE segue sua mesma função de excluir permanentemente esse objeto:
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:
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:
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:
ALTER SCHEMA meu_schema OWNER TO novo_proprietario;
E para excluí-lo é só utilizar o DROP 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:
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:
CREATE TABLE aplicacao1.tabela2_app1 (
id SERIAL PRIMARY KEY,
endereco VARCHAR(150)
);
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:
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:
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:
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):
-- 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:
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:
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:
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:
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:
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:
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’:
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:
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:
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:
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
12 de Agosto de 2024, 14:38 - sem comentários aindaIntroduçã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:
$ 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:
$ /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:
$ 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:
$ 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”):
$ 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:
$ 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 -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 -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):
$ 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:
$ psql --help
psql is the PostgreSQL interactive terminal.
Usage:
psql [OPTION]... [DBNAME [USERNAME]]
Um exemplo de acesso pelo Windows seria assim:
> & "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:
\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:
postgres=#
E outro com a configuração:
[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:
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
:
\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
:
# \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
:
# \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
:
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 *
:
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>
:
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:
$ 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
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):
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 tabelapg_class
, o primeiro com o\x
desativado, e o segundo com ele ativado):
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 |
...
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.