PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Use a saída de texto de uma função como nova consulta


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.