Isso é difícil de resolver, porque o SQL exige saber o tipo de retorno no momento da chamada .
Além disso, uma função plpgsql precisa ter um tipo de retorno bem definido .
Se você optar por retornar registros anônimos , você obtém o que definiu:registros anônimos. Postgres não sabe o que está dentro. Portanto, uma lista de definição de coluna é necessária para decompor o tipo.
Existem várias soluções alternativas, dependendo dos requisitos exatos. Se você tiver alguma maneira de saber o tipo de retorno no momento da chamada , sugiro tipos polimórficos conforme descrito no último capítulo desta resposta ("Vários tipos de tabela completa"):
Refatorar uma função PL/pgSQL para retornar a saída de várias consultas SELECT
Mas isso não cobre a adição de outra coluna ao tipo de retorno em tempo de execução dentro da função . Isso não é possível. Eu repensaria toda a abordagem .
Quanto à sua abordagem atual, a coisa mais próxima que consigo pensar seria uma tabela temporária (ou um cursor), que você consulta em uma segunda chamada dentro de uma transação única .
Você tem alguns outros problemas em seu código . Veja as notas abaixo.
Prova de conceito
CREATE OR REPLACE FUNCTION f_tbl_plus_infowindow (_tbl regclass) -- regclass!
RETURNS void AS -- no direct return type
$func$
DECLARE
-- appending _tmp for temp table
_tmp text := quote_ident(_tbl::text || '_tmp');
BEGIN
-- Create temp table only for duration of transaction
EXECUTE format(
'CREATE TEMP TABLE %s ON COMMIT DROP AS TABLE %s LIMIT 0', _tmp, _tbl);
IF EXISTS (
SELECT 1
FROM pg_attribute a
WHERE a.attrelid = _tbl
AND a.attname = 'infowindow'
AND a.attisdropped = FALSE)
THEN
EXECUTE format('INSERT INTO %s SELECT * FROM %s', _tmp, _tbl);
ELSE
-- This is assuming a NOT NULL column named "id"!
EXECUTE format($x$
ALTER TABLE %1$s ADD COLUMN infowindow text;
INSERT INTO %1$s
SELECT *, 'ID: ' || id::text
FROM %2$s $x$
,_tmp, _tbl);
END IF;
END
$func$ LANGUAGE plpgsql;
A chamada deve ser em uma única transação. Você pode ter que iniciar uma transação explícita, dependendo do seu cliente.
BEGIN;
SELECT f_tbl_plus_infowindow ('tbl');
SELECT * FROM tbl_tmp; -- do something with the returned rows
ROLLBACK; -- or COMMIT, does not matter here
SQL Fiddle.
Alternativamente, você pode deixar a tabela temporária ativa durante a sessão. No entanto, tenha cuidado com colisões de nomes com chamadas repetidas.
Observações
-
Use nomes de parâmetros em vez do desatualizadoALIAS
comando.
-
Para realmente "padrão" para o esquema atual, use a consulta mais simples que eu exponho. Usandoregclass
faz o truque automaticamente. Detalhes:
- Nome da tabela como parâmetro de função do PostgreSQL
Além disso, isso também evita erros de sintaxe e possível injeção de SQL de nomes de tabelas não padrão (ou malformados) em seu código original.
-
O código em seuELSE
cláusula não funcionaria.
-
TABLE tbl;
é basicamente a abreviação deSELECT * FROM tbl;
.
-
Detalhes sobreformat()
no manual.