O truque com
PREPARE
não funciona, pois não recebe uma * string de texto* (um valor) como CREATE FUNCTION
sim, mas uma instrução válida (código). Para converter dados em código executável você precisa usar SQL dinâmico, ou seja,
EXECUTE
em uma função plpgsql ou DO
demonstração. Isso funciona sem problemas desde que o tipo de retorno não dependa do resultado da primeira função myresult()
. Caso contrário, você está de volta para pegar 22, conforme descrito na minha resposta anterior:- Como executar um resultado de string de um procedimento armazenado no postgres
A parte crucial é declarar o tipo de retorno (tipo de linha neste caso) de alguma forma. Você pode criar uma
TABLE
, TEMP TABLE
ou TYPE
para o propósito. Ou você pode usar uma instrução preparada ou um refcursor. Solução com declaração preparada
Você tem estado muito perto. A peça que faltava no quebra-cabeça é preparar a consulta gerada com SQL dinâmico .
Função para preparar declaração dinamicamente
Crie esta função uma vez . É uma versão otimizada e segura da sua função
myresult()
:CREATE OR REPLACE FUNCTION f_prep_query (_tbl regclass, _prefix text)
RETURNS void AS
$func$
BEGIN
IF EXISTS (SELECT 1 FROM pg_prepared_statements WHERE name = 'stmt_dyn') THEN
DEALLOCATE stmt_dyn;
END IF; -- you my or may not need this safety check
EXECUTE (
SELECT 'PREPARE stmt_dyn AS SELECT '
|| string_agg(quote_ident(attname), ',' ORDER BY attname)
|| ' FROM ' || _tbl
FROM pg_catalog.pg_attribute
WHERE attrelid = _tbl
AND attname LIKE _prefix || '%'
AND attnum > 0
AND NOT attisdropped
);
END
$func$ LANGUAGE plpgsql;
Eu uso
regclass
para o parâmetro de nome da tabela _tbl
para torná-lo inequívoco e seguro contra SQLi. Detalhes:- Nome da tabela como parâmetro de função do PostgreSQL
O esquema de informações não inclui a coluna oid dos catálogos do sistema, então mudei para
pg_catalog.pg_attribute
em vez de information_schema.columns
. Isso é mais rápido também. Existem prós e contras para isso:- Como verificar se uma tabela existe em um determinado esquema
Se uma instrução preparada com o nome
stmt_dyn
já existia, PREPARE
abriria uma exceção. Se isso for aceitável, remova a verificação na visualização do sistema pg_prepared_statements
e o seguinte DEALLOCATE
.Algoritmos mais sofisticados são possíveis para gerenciar várias instruções preparadas por sessão, ou tomar o nome da instrução preparada como parâmetro adicional, ou ainda usar um hash MD5 da string de consulta como nome, mas isso está além do escopo desta pergunta.
Esteja ciente de que
PREPARE
opera fora do escopo das transações , uma vez PREPARE
for bem-sucedida, a instrução preparada existirá durante o tempo de vida da sessão. Se a transação de encapsulamento for abortada, PREPARE
não é afetado. ROLLBACK
não pode remover declarações preparadas. Execução de consulta dinâmica
Dois consultas, mas apenas uma ligue para o servidor. E muito eficiente também.
SELECT f_prep_query('tbl'::regclass, 'pre'::text);
EXECUTE stmt_dyn;
Mais simples e muito mais eficiente para a maioria dos casos de uso simples do que criar uma tabela temporária ou um cursor e selecionar / buscar a partir disso (o que seria outras opções).
SQL Fiddle.