Dette er svært at løse, fordi SQL kræver at kende returtypen på opkaldstidspunktet .
En plpgsql-funktion skal også have en veldefineret returtype .
Hvis du vælger at returnere anonyme poster , får du det, du definerede:anonyme optegnelser. Postgres ved ikke, hvad der er indeni. Derfor er en kolonnedefinitionsliste påkrævet for at dekomponere typen.
Der er forskellige løsninger, afhængigt af nøjagtige krav. Hvis du har nogen måde at kende returtypen på opkaldstidspunktet , foreslår jeg polymorfe typer som beskrevet i det sidste kapitel af dette svar ("Forskellige komplette tabeltyper"):
Refaktorer en PL/pgSQL-funktion for at returnere output fra forskellige SELECT-forespørgsler
Men det dækker ikke tilføjelse af endnu en kolonne til returtypen ved runtime inde i funktionen . Det er bare ikke muligt. Jeg ville genoverveje hele tilgangen .
Hvad angår din nuværende tilgang, ville det tætteste, jeg kan komme i tanke om, være en midlertidig tabel (eller en markør), som du forespørger på i et andet opkald inden for en enkelt transaktion .
Du har et par andre problemer i din kode . Se bemærkninger nedenfor.
Proof of concept
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;
Opkaldet skal være i en enkelt transaktion. Du skal muligvis starte en eksplicit transaktion, afhængigt af din klient.
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.
Alternativt kan du lade det midlertidige bord leve under sessionens varighed. Vær dog på vagt over for navnekollisioner med gentagne opkald.
Bemærkninger
-
Brug parameternavne i stedet for det forældede
ALIAS
kommando. -
For faktisk at "standard" til det aktuelle skema, brug den enklere forespørgsel, jeg viser. Brug af
regclass
gør tricket automatisk. Detaljer:- Tabelnavn som en PostgreSQL-funktionsparameter
Derudover undgår dette også syntaksfejl og mulig SQL-injektion fra ikke-standardiserede (eller ondsindede forkerte) tabelnavne i din originale kode.
-
Koden i din
ELSE
klausul ville slet ikke virke. -
TABLE tbl;
er grundlæggende en forkortelse forSELECT * FROM tbl;
. -
Detaljer om
format()
i manualen.