Questo è difficile da risolvere, perché SQL richiede di conoscere il tipo restituito al momento della chiamata .
Inoltre, una funzione plpgsql deve avere un tipo restituito ben definito .
Se scegli di restituire record anonimi , ottieni ciò che hai definito:record anonimi. Postgres non sa cosa c'è dentro. Pertanto, è necessario un elenco di definizioni di colonna per scomporre il tipo.
Esistono varie soluzioni alternative, a seconda dei requisiti esatti. Se hai modo di conoscere il tipo di reso al momento della chiamata , suggerisco tipi polimorfici come descritto nell'ultimo capitolo di questa risposta ("Vari tipi di tabelle complete"):
Refactoring di una funzione PL/pgSQL per restituire l'output di varie query SELECT
Ma ciò non copre l'aggiunta di un'altra colonna al tipo restituito in fase di esecuzione all'interno della funzione . Non è possibile. Riconsidererei l'intero approccio .
Per quanto riguarda il tuo approccio attuale, la cosa più vicina a cui riesco a pensare sarebbe una tabella temporanea (o un cursore), che interroghi in una seconda chiamata all'interno di una singola transazione .
Hai un paio di altri problemi nel codice . Vedi le note sotto.
Prova del concetto
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;
La chiamata deve essere in un'unica transazione. Potrebbe essere necessario avviare una transazione esplicita, a seconda del 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.
In alternativa, puoi lasciare in vita il tavolo temporaneo per la durata della sessione. Tuttavia, fai attenzione alle collisioni di nomi con chiamate ripetute.
Note
-
Usa i nomi dei parametri invece dell'obsoleto
ALIAS
comando. -
Per "predefinire" effettivamente lo schema corrente, usa la query più semplice che visualizzo. Usando
regclass
fa il trucco automaticamente. Dettagli:- Nome tabella come parametro di funzione PostgreSQL
Inoltre, questo evita anche errori di sintassi e possibili iniezione SQL da nomi di tabelle non standard (o malformati) nel codice originale.
-
Il codice nel tuo
ELSE
la clausola non funzionerebbe affatto. -
TABLE tbl;
è fondamentalmente l'abbreviazione diSELECT * FROM tbl;
. -
Dettagli su
format()
nel manuale.