Uma extensão útil para o PostgreSQL geralmente inclui vários objetos SQL; por exemplo, um novo tipo de dados exige novas funções, novos operadores e, provavelmente, novas classes de operador de índice. É útil coletar todos esses objetos em um único pacote para simplificar o gerenciamento do banco de dados. O PostgreSQL chama esse pacote de extensão. Para definir uma extensão, é necessário pelo menos um arquivo de script contendo comandos SQL para criar os objetos da extensão, e um arquivo de controle especificando algumas propriedades básicas da própria extensão. Se a extensão incluir código C, normalmente também haverá um arquivo de biblioteca compartilhada criado pelo código C. Após criar esses arquivos, um simples comando CREATE EXTENSION carrega os objetos no banco de dados.
A principal vantagem de usar uma extensão, em vez de simplesmente executar um script SQL para carregar um monte de objetos “soltos” no banco de dados, é que o PostgreSQL entende que esses objetos estão vinculados a essa extensão. Podem ser excluídos todos os objetos com um único comando DROP EXTENSION (não há necessidade de manter um script “uninstall” separado). Ainda mais útil, o pg_dump sabe que não deve descarregar na cópia de segurança os objetos membros individuais da extensão — em vez disso, será apenas incluído um comando CREATE EXTENSION nos arquivos de cópia de segurança criados (dumps). Isso simplifica muito a migração para uma nova versão da extensão, que pode conter mais objetos, ou objetos diferentes da versão antiga. No entanto, deve-se observar que o arquivo de controle, o script SQL, e outros arquivos da extensão, devem estar disponíveis ao carregar a cópia de segurança em um novo banco de dados.
O PostgreSQL não permite que se exclua
um objeto individual contido em uma extensão, exceto removendo
toda a extensão.
Além disso, embora se possa alterar a definição de um objeto membro
da extensão (por exemplo, via
CREATE OR REPLACE FUNCTION para uma função),
deve-se ter em mente que a definição modificada não será
descarregada pelo pg_dump.
Geralmente, essa alteração só faz sentido se for efetuada a mesma
alteração simultaneamente no arquivo de script da extensão.
(Mas existem provisões especiais para tabelas contendo dados de
configuração; veja Tabelas de configuração de extensão.)
Em situações de produção, é geralmente melhor criar um script de
atualização da extensão para realizar alterações em objetos membros
da extensão.
O script da extensão pode definir privilégios em objetos que fazem
parte da extensão, usando as instruções GRANT e
REVOKE.
O conjunto final de privilégios para cada objeto (se houver algum
definido) será armazenado no catálogo do sistema
pg_init_privs.
Quando é usado o pg_dump, o comando
CREATE EXTENSION será incluído na cópia de
segurança, seguido pelo conjunto de instruções
GRANT e REVOKE necessárias
para definir os privilégios nos objetos para como estavam no momento
em que a cópia de segurança foi criada.
No momento, o PostgreSQL não oferece
suporte a scripts de extensão que executam instruções
CREATE POLICY ou SECURITY LABEL.
Espera-se que eles sejam definidos após a criação da extensão.
Todas as políticas de RLS (segurança ao nível de
linha) e rótulos de segurança em objetos de extensão serão incluídos
nas exportações realizadas pelo pg_dump.
O mecanismo de extensão também possui provisões para scripts de
modificação de empacotamento que ajustam as definições dos objetos
SQL contidos em uma extensão.
Por exemplo, se a versão 1.1 de uma extensão adiciona uma função e
altera o corpo de outra função em comparação com a versão 1.0,
o autor da extensão pode fornecer um
script de atualização que faz apenas essas
duas alterações.
O comando ALTER EXTENSION UPDATE pode ser usado
para aplicar essas alterações, e rastrear qual versão da extensão
está realmente instalada em um determinado banco de dados.
Os tipos de objetos SQL que podem ser membros de uma extensão são mostrados na descrição do comando ALTER EXTENSION. Notadamente, os objetos que abrangem todo o agrupamento de bancos de dados (cluster), como bancos de dados, funções de banco de dados (roles), e espaços de tabela, não podem ser membros de extensão, porque uma extensão só é conhecida em um banco de dados. (Embora um script de extensão não esteja proibido de criar esses objetos, se o fizer, eles não serão rastreados como parte da extensão.) Note também que, embora uma tabela possa ser membro de uma extensão, seus objetos correlatos, como índices, não são considerados diretamente membros da extensão. Outro ponto importante é que esquemas podem pertencer a extensões, mas não o contrário: uma extensão como tal tem um nome não qualificado, e não existe “dentro” de nenhum esquema. Os objetos membros da extensão, no entanto, pertencerão aos esquemas sempre que for apropriado para seus tipos de objeto. Pode ou não ser apropriado para uma extensão possuir o(s) esquema(s) em que seus objetos membros se encontram.
Se o script de uma extensão criar quaisquer objetos temporários (como tabelas temporárias), esses objetos serão tratados como membros da extensão pelo restante da sessão corrente, mas serão descartados automaticamente no final da sessão, como qualquer objeto temporário seria. Essa é uma exceção à regra de que os objetos membro de extensão não podem ser excluídos sem remover toda a extensão.
O comando CREATE EXTENSION depende de um arquivo
de controle para cada extensão, que deve ter o mesmo nome da extensão
e o sufixo .control, e deve ser colocado no
diretório da instalação $SHAREDIR/extension.
Também deve haver pelo menos um arquivo de script
SQL, que segue o padrão de nomenclatura
(por exemplo, extensão--versão.sqlfoo--1.0.sql para a versão
1.0 da extensão foo).
Por padrão, os arquivos de script também são colocados no diretório
$SHAREDIR/extension; mas o arquivo de controle
pode especificar um diretório diferente para o(s) arquivo(s) de script.
Podem ser configurados locais adicionais para arquivos de controle de extensão usando o parâmetro extension_control_path.
O formato de arquivo para um arquivo de controle de extensão é o
mesmo do arquivo postgresql.conf, ou seja,
uma lista de atribuições nome_do_parâmetro
= valor, uma por linha.
São permitidas linhas em branco e comentários começados por
#.
Certifique-se de delimitar qualquer valor que não seja uma única
palavra ou número.
O arquivo de controle pode definir os seguintes parâmetros:
directory (string) #O diretório que contém o(s) arquivo(s) de script SQL da extensão. A menos que seja fornecido um caminho absoluto, o nome será relativo ao diretório onde o arquivo de controle foi encontrado. Por padrão, os arquivos de script são procurados no mesmo diretório onde o arquivo de controle foi encontrado.
default_version (string) #
A versão padrão da extensão (aquela que será instalada se
nenhuma versão for especificada no comando
CREATE EXTENSION).
Embora possa ser omitida, resultará em falha do comando
CREATE EXTENSION se nenhuma opção
VERSION for encontrada, então geralmente
não se deseja omiti-la.
comment (string) #Um comentário (qualquer cadeia de caracteres) sobre a extensão. O comentário é aplicado ao criar inicialmente a extensão, mas não durante as atualizações da extensão (já que poderia substituir os comentários adicionados pelo usuário). Como alternativa, o comentário da extensão pode ser definido escrevendo um comando COMMENT no arquivo de script SQL.
encoding (string) #A codificação do conjunto de caracteres usada pelo(s) arquivo(s) de script. Deve ser especificado se os arquivos de script contiverem quaisquer caracteres não ASCII. Caso contrário, os arquivos serão considerados estando na mesma codificação do banco de dados.
module_pathname (string) #
O valor desse parâmetro será substituído para cada ocorrência de
MODULE_PATHNAME no(s) arquivo(s) de script.
Se não estiver definido, nenhuma substituição será feita.
Normalmente, é definido como
$libdir/,
e então nome_da_biblioteca_compartilhadaMODULE_PATHNAME é usado nos comandos
CREATE FUNCTION para funções na linguagem
C, para que os arquivos de script não
precisem fixar o nome da biblioteca compartilhada.
requires (string) #
Uma lista de nomes de extensões das quais essa extensão depende,
por exemplo requires = 'foo, bar'.
Essas extensões devem ser instaladas antes que essa possa ser
instalada.
no_relocate (string) #
Uma lista de nomes de extensões das quais esta extensão depende
e que devem ser impedidas de alterar seus esquemas via
ALTER EXTENSION ... SET SCHEMA.
Isso é necessário caso o script desta extensão faça referência
ao nome do esquema (usando a sintaxe
@extschema: )
de uma extensão obrigatória de uma forma que não permita
rastrear a troca de nome.
nome@
superuser (boolean) #
Se esse parâmetro for definido como true
(que é o padrão), somente superusuários podem criar essa
extensão ou atualizá-la para uma nova versão
(mas veja também trusted, abaixo).
Se estiver definido como false, apenas os
privilégios necessários para executar os comandos no script
de instalação ou atualização serão necessários.
Normalmente, deve ser definido como true se
algum dos comandos do script exigir privilégios de superusuário.
(Esses comandos falhariam de qualquer maneira, mas é mais fácil
fornecer o erro antecipadamente.)
trusted (boolean) #
Se esse parâmetro for definido como true
(que não é o padrão), permite que alguns não-superusuários
instalem uma extensão que tenha superuser
definido como true.
Especificamente, será permitida a instalação por qualquer
pessoa que tenha o privilégio CREATE no
banco de dados corrente.
Quando o usuário que executa CREATE EXTENSION
não é um superusuário, mas tem permissão para instalar em virtude
desse parâmetro, o script de instalação ou atualização é executado
como o superusuário de inicialização, e não como o usuário que
está chamando o comando.
Esse parâmetro é irrelevante se superuser
for false.
Geralmente, esse parâmetro não deve ser definido como verdade
para extensões que podem permitir acesso a funcionalidades
de superusuário, como acesso ao sistema de arquivos.
Além disso, marcar uma extensão como confiável requer um esforço
extra significativo para escrever o(s) script(s) de instalação
e de atualização da extensão com segurança;
veja Considerações sobre segurança para extensões.
relocatable (boolean) #
Uma extensão é realocável se for possível
mover os objetos contidos nela para um esquema diferente após a
criação inicial da extensão.
O padrão é false, ou seja, a extensão
não é realocável.
Veja Relocabilidade de extensão
para obter mais informações.
schema (string) #
Esse parâmetro só pode ser definido para extensões não relocáveis.
Esse parâmetro força a extensão ser carregada exatamente no
esquema indicado, e não em nenhum outro.
O parâmetro schema é consultado apenas ao
criar inicialmente a extensão, não sendo consultado durante
as atualizações da extensão.
Veja Relocabilidade de extensão
para obter mais informações.
Além do arquivo de controle primário
,
a extensão pode ter arquivos de controle secundários com nomes no estilo
extensão.control.
Se fornecidos, devem estar localizados no diretório do arquivo de script.
Os arquivos de controle secundários seguem o mesmo formato do
arquivo de controle primário.
Quaisquer parâmetros definidos no arquivo de controle secundário
substituem os do arquivo de controle primário, ao instalar ou
atualizar para essa versão da extensão.
Entretanto, os parâmetros extensão--versão.controldirectory e
default_version não podem ser definidos em
arquivos de controle secundários.
Os arquivos de script SQL de uma extensão podem
conter qualquer comando SQL, exceto comandos
para controle de transação (BEGIN,
COMMIT, etc.)
e comandos que não podem ser executados dentro de um bloco de
transação (como VACUUM).
Isso se dá porque os arquivos de script são executados
implicitamente em um bloco de transação.
Os arquivos de script SQL de uma extensão também
podem conter linhas começando com \echo, que são
ignoradas (tratadas como comentários) pelo mecanismo de extensão.
Essa provisão é comumente usada para lançar um erro se o arquivo
de script for alimentado para o psql,
em vez de ser carregado via CREATE EXTENSION
(veja o script de exemplo em
Exemplo de extensão).
Sem isso, os usuários podem acidentalmente carregar o conteúdo da
extensão como objetos “soltos”, em vez de em uma
extensão, um estado de coisas que é um tanto entediante para se
recuperar dele.
Se o script de extensão contiver a cadeia de caracteres
@extowner@, essa cadeia de caracteres será
substituída pelo nome (adequadamente delimitado) do usuário que
está chamando CREATE EXTENSION ou
ALTER EXTENSION.
Normalmente, esse recurso é usado por extensões marcadas como
confiáveis, para atribuir a propriedade dos objetos selecionados ao
usuário que efetua a chamada, em vez do superusuário de inicialização.
(No entanto, deve-se ter cuidado ao fazer isso.
Por exemplo, atribuir como dono de uma função na linguagem
C um não superusuário criaria um caminho
de escalonamento de privilégio para esse usuário.)
Embora os arquivos de script possam conter quaisquer caracteres
permitidos pela codificação especificada, os arquivos de controle
devem conter apenas caracteres ASCII simples, porque não há como o
PostgreSQL saber em qual codificação
um arquivo de controle está.
Na prática, isso só é um problema se for desejado usar caracteres
não ASCII no comentário da extensão.
A prática recomendada nesse caso é não usar o parâmetro
comment do arquivo de controle, e sim usar
COMMENT ON EXTENSION em um arquivo de script
para definir o comentário.
Os usuários geralmente desejam carregar os objetos contidos em uma extensão em um esquema diferente do que o autor da extensão tinha em mente. Existem três níveis de realocabilidade com suporte:
Uma extensão totalmente realocável pode ser movida para outro
esquema a qualquer momento, mesmo após ter sido carregada
em um banco de dados. Isso é feito com o comando
ALTER EXTENSION SET SCHEMA, que muda o nome
de todos os objetos membros para o novo esquema automaticamente.
Normalmente, só é possível se a extensão não contiver suposições
internas sobre qual esquema qualquer um de seus objetos está.
Além disso, os objetos da extensão devem estar todos no mesmo
esquema para começar (ignorando objetos que não pertencem a
nenhum esquema, como linguagens procedurais).
Uma extensão totalmente realocável deve ser marcada definindo
relocatable = true em seu arquivo de controle.
A extensão pode ser realocável durante a instalação, mas não
após ter sido instalada.
Normalmente, esse é o caso se o arquivo de script da extensão
precisar fazer referência explicitamente ao esquema de destino,
por exemplo, ao configurar as propriedades
search_path para funções SQL.
Para esse tipo de extensão, deve ser definido
relocatable = false em seu arquivo de controle,
e usado @extschema@ para fazer referência ao
esquema de destino no arquivo de script.
Todas as ocorrências dessa cadeia de caracteres serão substituídas
pelo nome do esquema (entre aspas, se for necessário) de destino
real antes que o script seja executado.
O usuário pode definir o esquema de destino usando a opção
SCHEMA do comando
CREATE EXTENSION.
Se a extensão não permitir relocação, deve ser definido
relocatable = false em seu arquivo de controle,
e também definido o parâmetro schema para o
nome do esquema de destino pretendido.
Isso impedirá o uso da opção SCHEMA no comando
CREATE EXTENSION, a menos que seja especificado
o mesmo esquema indicado no arquivo de controle.
Geralmente é necessária essa escolha se a extensão contiver
suposições internas sobre nomes de esquema que não podem ser
substituídos por usos de @extschema@.
O mecanismo de substituição @extschema@ também
está disponível nesse caso, embora tenha seu uso limitado, porque
o nome do esquema é determinado pelo arquivo de controle.
Em todos esses casos, o arquivo de script será executado com
search_path inicialmente definido
apontando para o esquema de destino; ou seja,
CREATE EXTENSION faz o equivalente a:
SET LOCAL search_path TO @extschema@, pg_temp;
Isso permite os objetos criados pelo arquivo de script irem
para o esquema de destino.
O arquivo de script pode mudar search_path
se quiser, mas isso é geralmente indesejável.
O search_path é restaurado para sua configuração
anterior após a conclusão de CREATE EXTENSION.
O esquema de destino é determinado pelo parâmetro
schema no arquivo de controle, se for fornecido,
caso contrário pela opção SCHEMA do comando
CREATE EXTENSION, se estiver presente,
e caso contrário o esquema de criação de objeto padrão corrente
(o primeiro no search_path do usuário que efetua
a chamada).
Quando é usado o parâmetro schema do arquivo
de controle, o esquema de destino será criado se ainda não existir,
mas nos outros dois casos o esquema já deve existir.
Se alguma extensão que seja pré-requisito estiver listada no
parâmetro requires no arquivo de controle,
seus esquemas de destino serão adicionados à configuração inicial
de search_path, seguindo o novo esquema de
destino da extensão.
Isso permite que seus objetos fiquem visíveis para o arquivo de
script da nova extensão.
Por segurança, pg_temp é anexado automaticamente
ao final de search_path em todos os casos.
Embora uma extensão não realocável possa conter objetos espalhados
por vários esquemas, é geralmente desejável colocar todos os objetos
destinados ao uso externo em um único esquema, considerado
o esquema de destino da extensão.
Esse arranjo funciona convenientemente com a configuração padrão de
search_path durante a criação de extensões
dependentes.
Se uma extensão fizer referência a objetos pertencentes a outra
extensão, recomenda-se qualificar essas referências de acordo
com o esquema.
Para fazer isso, deve ser escrito
@extschema:
no arquivo de script da extensão, onde nome@nome
é o nome da outra extensão (que deve ser listado na lista
requires desta extensão).
Essa sequência de caracteres será substituída pelo nome
(entre aspas, se necessário) do esquema de destino dessa extensão.
Embora essa notação evite a necessidade de fazer suposições fixadas
(hard-wired) sobre os nomes de
esquema no arquivo de script da extensão, seu uso pode incorporar
o nome do esquema da outra extensão nos objetos instalados desta
extensão.
(Normalmente isso acontece quando
@extschema:
é usado dentro de uma cadeia de caracteres literal, como o corpo
de uma função ou uma configuração de nome@search_path.
Em outros casos, a referência ao objeto é reduzida a um OID durante
a análise sintática e não requer pesquisas subsequentes.)
Se o nome do esquema da outra extensão estiver incorporado dessa
forma, deve-se impedir que a outra extensão seja realocada após a
instalação desta extensão, adicionando o nome da outra extensão
à lista no_relocate desta extensão.
Algumas extensões incluem tabelas de configuração, contendo dados que podem ser adicionados ou alterados pelo usuário após a instalação da extensão. Normalmente, se uma tabela faz parte de uma extensão, nem a definição da tabela, nem seu conteúdo, serão copiados pelo pg_dump. Mas esse comportamento não é desejado para uma tabela de configuração; quaisquer alterações de dados feitas pelo usuário precisam ser incluídas nas descargas, ou a extensão se comportará de maneira diferente após uma exportação e restauração (dump/restore).
Para resolver esse problema, o arquivo de script de uma extensão
pode marcar uma tabela, ou uma sequência que ela criou, como uma
relação de configuração, o que fará com que a aplicação
pg_dump inclua o conteúdo da tabela
ou da sequência (não a sua definição) nas cópias de segurança.
Para fazer isso, deve ser chamada a função
pg_extension_config_dump(regclass, text)
após a tabela ou a sequência ser criada. Por exemplo:
CREATE TABLE my_config (key text, value text);
CREATE SEQUENCE my_config_seq;
SELECT pg_catalog.pg_extension_config_dump('my_config', '');
SELECT pg_catalog.pg_extension_config_dump('my_config_seq', '');
Qualquer número de tabelas ou sequências pode ser marcado dessa
maneira. As sequências associadas às colunas serial
ou bigserial também podem ser marcadas.
Quando o segundo argumento da função
pg_extension_config_dump for uma cadeia de
caracteres vazia, todo o conteúdo da tabela é copiado por
pg_dump.
Isso geralmente só é correto se a tabela estiver inicialmente vazia
quando criada pelo script de extensão.
Se houver uma mistura de dados iniciais e dados fornecidos pelo
usuário na tabela, o segundo argumento da função
pg_extension_config_dump fornece uma condição
WHERE que seleciona os dados a serem descarregados.
Por exemplo, pode ser feito
CREATE TABLE my_config (key text, value text, standard_entry boolean);
SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry');
para certificar-se de que standard_entry
seja verdade apenas nas linhas criadas pelo script da extensão.
Para as sequências, o segundo argumento da função
pg_extension_config_dump não tem efeito.
Situações mais complicadas, como linhas fornecidas inicialmente que podem ser modificadas pelos usuários, podem ser tratadas criando gatilhos na tabela de configuração para garantir que as linhas modificadas sejam marcadas corretamente.
Pode ser alterada a condição do filtro associada a uma tabela de
configuração chamando a função
pg_extension_config_dump novamente.
(Isso normalmente seria útil em um script de atualização de extensão.)
A única maneira de marcar uma tabela como não sendo mais uma tabela
de configuração é dissociá-la da extensão com o comando
ALTER EXTENSION ... DROP TABLE.
Note que os relacionamentos de chaves estrangeiras entre essas
tabelas ditarão a ordem na qual as tabelas serão descarregadas pelo
pg_dump.
Especificamente, pg_dump tentará
descarregar a tabela referenciada antes da tabela que faz a
referência.
Como os relacionamentos de chave estrangeira são configurados no
momento do comando CREATE EXTENSION
(antes dos dados serem carregados nas tabelas)
não há suporte para dependências circulares.
Quando existirem dependências circulares, os dados ainda serão
descarregados, mas a descarga não poderá ser recuperada diretamente,
sendo necessária intervenção do usuário.
As sequências associadas às colunas serial ou
bigserial precisam ser marcadas diretamente para
exportar o seu estado.
Marcar sua relação mãe não é suficiente para esse propósito.
Uma vantagem do mecanismo de extensão é o fornecimento de maneiras
convenientes de gerenciar as atualizações para os comandos
SQL que definem os objetos de uma extensão.
Isso é feito associando um nome ou número de versão a cada versão
lançada do script de instalação da extensão.
Além disso, se for desejado que os usuários possam atualizar seus
bancos de dados dinamicamente de uma versão para outra, devem ser
fornecidos scripts de atualização que façam
as alterações necessárias para ir de uma versão para outra.
Os scripts de atualização têm nomes seguindo o padrão
(por exemplo, extensão--versão_antiga--versão_de_destino.sqlfoo--1.0--1.1.sql contém os
comandos para modificar a versão 1.0 da extensão
foo para a versão 1.1).
Uma vez que um script de atualização adequado está disponível,
o comando ALTER EXTENSION UPDATE atualizará
a extensão instalada para a nova versão especificada.
O script de atualização é executado no mesmo ambiente que o comando
CREATE EXTENSION fornece para scripts de
instalação: em particular, search_path é
configurado da mesma forma, e quaisquer novos objetos criados pelo
script são adicionados automaticamente à extensão.
Além disso, se o script optar por excluir objetos membro da
extensão, eles serão automaticamente dissociados da extensão.
Se a extensão tiver arquivos de controle secundários, os parâmetros de controle usados para o script de atualização serão aqueles associados à versão de destino (nova) do script.
O comando ALTER EXTENSION pode executar
sequências de arquivos de script de atualização para obter a
atualização solicitada.
Por exemplo, se estirem disponíveis apenas
foo--1.0--1.1.sql e
foo--1.1--2.0.sql, o comando
ALTER EXTENSION irá aplicá-los em sequência,
se a atualização para a versão 2.0 for
solicitada quando a 1.0 estiver instalada.
O PostgreSQL não assume nada sobre as
propriedades dos nomes das versões: por exemplo, não sabe se
1.1 segue 1.0.
Ele apenas corresponde aos nomes das versões disponíveis, seguindo
o caminho que requer a aplicação do menor número de scripts de
atualização.
(Um nome de versão pode, na verdade, ser qualquer cadeia de
caracteres que não contenha os caracteres --,
ou o caractere - à esquerda ou à direita.)
Às vezes é útil fornecer scripts de “reversão”
(downgrade), por exemplo,
foo--1.1--1.0.sql para permitir a reversão
das alterações associadas à versão 1.1.
Se isso for feito, deve-se tomar cuidado com a possibilidade
de um script de reversão ser aplicado inesperadamente,
porque gera um caminho mais curto.
O caso de risco é quando há um script de atualização
“rápido” que avança várias versões, bem como um
script de reversão para o ponto inicial do caminho rápido.
Podem ser necessárias menos etapas para aplicar a reversão e,
em seguida, aplicar o caminho rápido, do que avançar uma versão
por vez.
Se o script de reversão excluir quaisquer objetos insubstituíveis,
isso produzirá resultados indesejáveis.
Para verificar caminhos de atualização inesperados, use o comando:
SELECT * FROM pg_extension_update_paths('nome_da_extensão');
Esse comando mostra cada par de nomes de versão conhecidos distintos
para a extensão especificada, junto com a sequência de caminho de
atualização que seria usada para ir da versão de origem para a
versão de destino, ou NULL, caso não haja
caminho de atualização disponível.
O caminho é mostrado em forma de texto com separadores
--.
Pode ser usado regexp_split_to_array(path,'--')
se for preferido um formato de matriz.
Uma extensão que já existe há algum tempo provavelmente existirá em
várias versões, para as quais o autor precisará escrever scripts de
atualização.
Por exemplo, se foi lançada a extensão foo
nas versões 1.0, 1.1, e
1.2, devem existir os scripts de atualização
foo--1.0--1.1.sql e
foo--1.1--1.2.sql.
Antes do PostgreSQL 10, também era
necessário criar os novos arquivos de script
foo--1.1.sql e foo--1.2.sql
que construíam diretamente as versões mais recentes da extensão,
ou então as versões mais recentes não poderiam ser instaladas
diretamente, apenas instalando a 1.0 e depois
atualizando.
Isso era entediante e duplicado, mas agora é desnecessário, porque
o comando CREATE EXTENSION pode seguir as cadeias
de atualização automaticamente.
Por exemplo, se apenas os arquivos de script
foo--1.0.sql, foo--1.0--1.1.sql,
e foo--1.1--1.2.sql estiverem disponíveis,
uma solicitação para instalar a versão 1.2
será honrada executando esses três scripts em sequência.
O processamento é o mesmo como se tivesse sido instalado primeiro a
1.0, e depois atualizado para 1.2.
(Como em ALTER EXTENSION UPDATE, se estiverem
disponíveis vários caminhos, o mais curto será o preferido.)
Organizar os arquivos de script de uma extensão nesse estilo pode
reduzir a quantidade de esforço de manutenção necessária para
produzir pequenas atualizações.
Se forem usados arquivos de controle secundários (específicos da
versão) com uma extensão mantida nesse estilo, lembre-se de que
cada versão precisa de um arquivo de controle, mesmo que não tenha
um script de instalação autônomo, porque esse arquivo de controle
determinará como a atualização implícita para essa versão é executada.
Por exemplo, se foo--1.0.control especificar
requires = 'bar', mas os outros arquivos de
controle de foo não, a dependência da extensão
em bar será descartada ao atualizar de
1.0 para outra versão.
As extensões amplamente distribuídas devem presumir pouco sobre o banco de dados onde residem. Portanto, é apropriado escrever funções fornecidas por uma extensão em um estilo seguro, que não possa ser comprometido por ataques baseados em caminhos de procura.
Uma extensão com a propriedade superuser
definida como verdade, também deve considerar os riscos de segurança
para as ações executadas em seus scripts de instalação e atualização.
Não é muito difícil para um usuário mal-intencionado criar objetos
Cavalo de Troia que vão comprometer a execução posterior de um
script de extensão escrito de forma descuidada, permitindo que o
usuário adquira privilégios de superusuário.
Se uma extensão estiver marcada como trusted,
então seu esquema de instalação pode ser selecionado pelo usuário
que está instalando, que pode intencionalmente usar um esquema
inseguro na esperança de obter privilégios de superusuário.
Portanto, uma extensão confiável é extremamente exposta do
ponto de vista da segurança, e todos os seus comandos de script
devem ser cuidadosamente examinados para garantir que nenhum
comprometimento seja possível.
São fornecidos conselhos sobre como escrever funções seguras em Considerações sobre segurança para funções de extensão abaixo, e conselhos sobre como escrever scripts de instalação seguros são fornecidos em Considerações sobre segurança para scripts de extensão.
As funções em linguagem SQL e procedurais fornecidas por extensões correm o risco de ataques baseados em caminho de procura quando são executadas, porque a análise dessas funções ocorre no momento da execução, e não no momento da criação.
A página de referência de
CREATE FUNCTION
contém conselhos sobre como escrever funções
SECURITY DEFINER com segurança.
É uma boa prática aplicar essas técnicas para qualquer função
fornecida por uma extensão, porque a função pode ser chamada
por um usuário com privilégio elevado.
Se não puder ser definido o search_path
de modo a conter apenas esquemas seguros, deve-se supor que cada
nome não qualificado possa ser resolvido para um objeto definido
por um usuário mal-intencionado.
Deve-se tomar cuidado com as construções que dependem
implicitamente de search_path;
por exemplo, IN e
CASE
sempre selecionam o operador usando o caminho de procura.
Em seu lugar, deve ser usado
expressão WHENOPERATOR(
e esquema.=) ANYCASE WHEN .
expressão
Uma extensão de uso geral geralmente não deve presumir que foi
instalada em um esquema seguro, o que significa que mesmo as
referências qualificadas pelo esquema a seus próprios objetos
não são totalmente isentas de riscos.
Por exemplo, se a extensão tiver definido a função
meu_esquema.minha_função(bigint) então uma
chamada como meu_esquema.minha_função(42)
pode ser capturada por uma função hostil
meu_esquema.minha_função(integer).
Tenha cuidado para que os tipos de dados da função e os parâmetros
do operador correspondam exatamente aos tipos de dados dos
argumento declarados, usando conversões explícitas quando necessário.
Um script de instalação ou atualização de extensão deve ser escrito para proteger contra ataques baseados em caminho de procura que ocorrem quando o script é executado. Se uma referência a objeto no script puder ser resolvida para algum outro objeto que não seja o pretendido pelo autor do script, poderá ocorrer um comprometimento imediatamente ou mais tarde, quando o objeto da extensão mal definido for usado.
Os comandos DDL como
CREATE FUNCTION e
CREATE OPERATOR CLASS são geralmente seguros,
mas deve-se tomar cuidado com qualquer comando que tenha uma
expressão de uso geral como componente.
Por exemplo, CREATE VIEW precisa ser examinado,
assim como uma expressão DEFAULT em
CREATE FUNCTION.
Às vezes, um script de extensão pode precisar executar um comando
SQL de propósito geral, por exemplo, para fazer
ajustes no catálogo que não são possíveis via DDL.
Deve-se ter o cuidado de executar esses comandos com um
search_path seguro; não
se deve confiar no caminho fornecido por
CREATE/ALTER EXTENSION como sendo seguro.
A melhor prática é definir temporariamente o
search_path como
'pg_catalog, pg_temp', e inserir referências ao
esquema de instalação da extensão explicitamente quando necessário.
(Essa prática também pode ser útil para criar visões.)
Podem ser encontrados exemplos nos módulos
contrib na distribuição do código-fonte do
PostgreSQL.
Referências seguras entre extensões geralmente exigem a
qualificação de esquema dos nomes dos objetos da outra extensão,
usando a sintaxe
@extschema:,
além da cuidadosa correspondência dos tipos de dados dos
argumentos para funções e operadores.
name@
A seguir está um exemplo completo de uma extensão escrita somente em SQL, contendo um tipo de dados composto por dois elementos, que pode armazenar valor de qualquer tipo de dados em seus encaixes (slots), denominados “k” e “v”. Os valores não textuais são convertidos automaticamente para texto para armazenamento.
O arquivo de script pair--1.0.sql tem a
seguinte aparência:
-- reclamar se o script for executado no psql, em vez de via CREATE EXTENSION
\echo Use "CREATE EXTENSION pair" para carregar esse arquivo. \quit
CREATE TYPE pair AS ( k text, v text );
CREATE FUNCTION pair(text, text)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::@extschema@.pair;';
CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, FUNCTION = pair);
-- "SET search_path" é fácil de dar certo, mas nomes qualificados funcionam melhor.
CREATE FUNCTION lower(pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW(lower($1.k), lower($1.v))::@extschema@.pair;'
SET search_path = pg_temp;
CREATE FUNCTION pair_concat(pair, pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k,
$1.v OPERATOR(pg_catalog.||) $2.v)::@extschema@.pair;';
O arquivo de controle pair.control se parece com:
# extensao pair comment = 'Tipo de dados de par chave/valor' default_version = '1.0' # não pode ser realocável devido ao uso de @extschema@ relocatable = false
Embora dificilmente seja necessário um arquivo construtor para
instalar esses dois arquivos no diretório correto, pode
ser usado um arquivo de construção Makefile
contendo:
EXTENSION = pair DATA = pair--1.0.sql PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS)
Esse arquivo de construção depende de PGXS,
que é descrito em Infraestrutura para construção de extensão.
O comando make install irá instalar os arquivos
de controle e script no diretório correto, conforme informado por
pg_config.
Depois que os arquivos estiverem instalados, deve ser usado o
comando CREATE EXTENSION para carregar os
objetos em qualquer banco de dados específico.