Quando crio um novo schema no Postgres através da execução de um script em psql
quero colocá-lo em um tablespace. Se esse espaço de tabela não existir, quero criá-lo primeiro. Como o SQL padrão não tem essa opção criei uma função:
CREATE OR REPLACE FUNCTION make_tablespace(tablespace CHARACTER,
directory CHARACTER,
owner CHARACTER)
RETURNS void
AS
$$
BEGIN
IF tablespace = '' THEN
RAISE EXCEPTION 'No tablespace.';
END IF;
PERFORM SPCNAME FROM PG_TABLESPACE WHERE SPCNAME=tablespace;
IF NOT FOUND THEN
IF directory = '' THEN
RAISE EXCEPTION 'No directory.';
END IF;
IF owner = '' THEN
RAISE EXCEPTION 'No owner.';
END IF;
EXECUTE 'CREATE TABLESPACE '||tablespace||' OWNER '||owner||' LOCATION '''||directory||''';';
RAISE NOTICE 'Tablespace % created.', tablespace;
ELSE
RAISE NOTICE 'Tablespace % already exists.', tablespace;
END IF;
END $$ LANGUAGE plpgsql;
Infelizmente quando executo ( select make_tablespace('marco', '/opt/marco', 'marco');
) dá um erro:
ERRO: CREATE TABLESPACE não pode ser executado a partir de uma função ou string de vários comandos
Pesquisei na internet e parece haver uma solução alternativa (alguns anos atrás) usando o dblink
pacote. Eu não quero instalar isso. Existe hoje outra maneira? Posso retornar a instrução SQL como uma string, mas como executá-la?
O ponto aqui é que as funções do Postgres são quase, mas não exatamente, procedimentos armazenados verdadeiros. As funções do Postgres (ao contrário dos procedimentos armazenados) são executadas no contexto de uma transação externa. Portanto, você não pode executar comandos que não podem ser executados em um bloco de transação, como
VACUUM
,CREATE DATABASE
, ... ouCREATE TABLESPACE
. O manual é claro sobre isso:Você precisa executar esses comandos como comandos SQL singulares. Postgres atualmente (até a versão 9.6) também não possui transações autônomas , o que poderia ser uma solução alternativa.
Portanto, a única solução alternativa para incluir o comando em uma função ou transação é simular uma transação autônoma com
dblink
, assim como você já encontrou.Existem alguns outros problemas menores em seu código. Eu sugiro:
Pontos principais:
character
. Você quertext.
''
, verifique tambémNULL
.format()
para código SQL dinâmico curto e limpo e escape adequadamente da entrada do usuário para evitar erros de sintaxe sorrateiros e injeção de SQL.Resposta relacionada com exemplos de código e mais explicações:
Inserções persistentes em um UDF mesmo se a função for abortada
O Postgres suporta transações aninhadas ou autônomas?