36.17. Empacotamento de objetos relacionados em uma extensão #

36.17.1. Arquivos da extensão
36.17.2. Relocabilidade de extensão
36.17.3. Tabelas de configuração de extensão
36.17.4. Atualizações de extensão
36.17.5. Instalação de extensão usando scripts de atualização
36.17.6. Considerações sobre segurança para extensões
36.17.7. Exemplo de extensão

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.

36.17.1. Arquivos da 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 extensão--versão.sql (por exemplo, foo--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/nome_da_biblioteca_​compartilhada, e então MODULE_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:nome@ ) de uma extensão obrigatória de uma forma que não permita rastrear a troca de 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 extensão.control, a extensão pode ter arquivos de controle secundários com nomes no estilo extensão--versã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 directory 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.

36.17.2. Relocabilidade de extensão #

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:nome@ no arquivo de script da extensão, onde 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:nome@ é usado dentro de uma cadeia de caracteres literal, como o corpo de uma função ou uma configuração de 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.

36.17.3. Tabelas de configuração de 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.

36.17.4. Atualizações de extensão #

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 extensão--versão_antiga--versão_de_destino.sql (por exemplo, foo--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.

36.17.5. Instalação de extensão usando scripts de atualização #

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.

36.17.6. Considerações sobre segurança para extensões #

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.

36.17.6.1. Considerações sobre segurança para funções 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 expressão WHEN sempre selecionam o operador usando o caminho de procura. Em seu lugar, deve ser usado OPERATOR(esquema.=) ANY e CASE 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.

36.17.6.2. Considerações sobre segurança para scripts de extensão #

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:name@, além da cuidadosa correspondência dos tipos de dados dos argumentos para funções e operadores.

36.17.7. Exemplo de extensão #

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.