Dit is moeilijk op te lossen, omdat SQL vereist om het retourtype te weten tijdens de oproep .
Ook moet een plpgsql-functie een goed gedefinieerd retourtype hebben .
Als u ervoor kiest om anonieme records te retourneren , krijg je wat je hebt gedefinieerd:anonieme records. Postgres weet niet wat erin zit. Daarom is een kolomdefinitielijst vereist om het type te ontleden.
Er zijn verschillende oplossingen, afhankelijk van de exacte vereisten. Als je een manier hebt om het retourtype te weten tijdens het bellen , stel ik polymorfe typen voor zoals uiteengezet in het laatste hoofdstuk van dit antwoord ("Verschillende complete tabeltypen"):
Refactor een PL/pgSQL-functie om de uitvoer van verschillende SELECT-query's te retourneren
Maar dat dekt niet het toevoegen van een andere kolom aan het retourtype at runtime binnen de functie . Dat kan gewoon niet. Ik zou de hele aanpak heroverwegen .
Wat betreft uw huidige aanpak, het dichtst in de buurt dat ik kan bedenken, is een tijdelijke tabel (of een cursor), die u in een tweede oproep opvraagt binnen een enkele transactie .
Je hebt een aantal andere problemen met je code . Zie onderstaande opmerkingen.
Bewijs van 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;
De oproep moet in een enkele transactie zijn. Het kan zijn dat u een expliciete transactie moet starten, afhankelijk van uw klant.
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.
Als alternatief kunt u de tijdelijke tafel laten leven voor de duur van de sessie. Wees echter op uw hoede voor het benoemen van botsingen met herhaalde oproepen.
Opmerkingen
-
Gebruik parameternamen in plaats van de verouderde
ALIAS
commando. -
Gebruik de eenvoudigere query die ik weergeef om daadwerkelijk "standaard" te zijn in het huidige schema.
regclass
gebruiken doet de truc automatisch. Details:- Tabelnaam als een PostgreSQL-functieparameter
Bovendien vermijdt dit ook syntaxisfouten en mogelijke SQL-injectie van niet-standaard (of kwaadwillig misvormde) tabelnamen in je originele code.
-
De code in uw
ELSE
clausule zou helemaal niet werken. -
TABLE tbl;
is in feite een afkorting voorSELECT * FROM tbl;
. -
Details over
format()
in de handleiding.