Jest to trudne do rozwiązania, ponieważ SQL wymaga znajomości typu zwracanego w czasie połączenia .
Ponadto funkcja plpgsql musi mieć dobrze zdefiniowany typ zwracany .
Jeśli zdecydujesz się zwrócić anonimowe rekordy , otrzymujesz to, co zdefiniowałeś:anonimowe rekordy. Postgres nie wie, co jest w środku. Dlatego lista definicji kolumn jest wymagana rozłożyć typ.
Istnieją różne obejścia, w zależności od dokładnych wymagań. Jeśli możesz w jakikolwiek sposób poznać typ zwrotu w czasie połączenia , proponuję typy polimorficzne jak opisano w ostatnim rozdziale tej odpowiedzi ("Różne typy kompletnych tabel"):
Refaktoryzacja funkcji PL/pgSQL w celu zwrócenia wyników różnych zapytań SELECT
Ale to nie obejmuje dodawania kolejnej kolumny do zwracanego typu w czasie wykonywania wewnątrz funkcji . To po prostu niemożliwe. Chciałbym przemyśleć całe podejście .
Jeśli chodzi o twoje obecne podejście, najbliższą rzeczą, o której mogę pomyśleć, byłaby tabela tymczasowa (lub kursor), do której wysyłasz zapytanie w drugim wywołaniu w ramach pojedynczej transakcji .
Masz kilka innych problemów w swoim kodzie . Zobacz uwagi poniżej.
Dowód koncepcji
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;
Połączenie musi być w ramach jednej transakcji. Być może będziesz musiał rozpocząć wyraźną transakcję, w zależności od klienta.
BEGIN;
SELECT f_tbl_plus_infowindow ('tbl');
SELECT * FROM tbl_tmp; -- do something with the returned rows
ROLLBACK; -- or COMMIT, does not matter here
Skrzypce SQL.
Alternatywnie możesz pozostawić tymczasową tabelę na żywo na czas trwania sesji. Uważaj jednak na kolizje nazw przy powtarzających się wywołaniach.
Notatki
-
Użyj nazw parametrów zamiast przestarzałego
ALIAS
polecenie. -
Aby faktycznie „domyślnie” w bieżącym schemacie, użyj prostszego zapytania, które wyświetlam. Korzystanie z
regclass
robi sztuczkę automatycznie. Szczegóły:- Nazwa tabeli jako parametr funkcji PostgreSQL
Ponadto pozwala to również uniknąć błędów składniowych i możliwego wstrzyknięcia SQL z niestandardowych (lub złośliwie zniekształconych) nazw tabel w oryginalnym kodzie.
-
Kod w Twoim
ELSE
klauzula w ogóle nie zadziała. -
TABLE tbl;
jest w zasadzie skrótem odSELECT * FROM tbl;
. -
Szczegóły dotyczące
format()
w instrukcji.