Това е трудно за решаване, тъй като SQL изисква да знае типа на връщане по време на повикване .
Освен това, функцията plpgsql трябва да има добре дефиниран тип връщане .
Ако изберете да върнете анонимни записи , получавате това, което сте дефинирали:анонимни записи. Postgres не знае какво има вътре. Следователно списък с дефиниции на колони е задължителен за разлагане на типа.
Има различни решения, в зависимост от точните изисквания. Ако имате някакъв начин да разберете типа връщане по време на разговор , предлагамполиморфни типове както е посочено в последната глава на този отговор („Различни типове пълни таблици“):
Рефакторирайте функция PL/pgSQL, за да върнете резултатите от различни SELECT заявки
Но това не обхваща добавянето на друга колона към типа на връщане по време на изпълнение във функцията . Това просто не е възможно. Бихи преразгледал целия подход .
Що се отнася до настоящия ви подход, най-близкото нещо, за което се сещам, е временна таблица (или курсор), която заявявате при второ обаждане в рамките на единична транзакция .
Имате няколко други проблема в кода си . Вижте бележките по-долу.
Доказателство за концепция
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;
Обаждането трябва да бъде в една транзакция. Може да се наложи да започнете изрична транзакция, в зависимост от вашия клиент.
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.
Като алтернатива можете да оставите временната маса да работи за времето на сесията. Все пак внимавайте при сблъсъци с имена с повтарящи се повиквания.
Бележки
-
Използвайте имена на параметри вместо остарелия
ALIAS
команда. -
За действително "по подразбиране" за текущата схема, използвайте по-простата заявка, която показвам. Използване на
regclass
прави трика автоматично. Подробности:- Име на таблица като параметър на функцията на PostgreSQL
В допълнение, това също така избягва синтактични грешки и възможно SQL инжектиране от нестандартни (или злонамерено деформирани) имена на таблици в оригиналния ви код.
-
Кодът във вашия
ELSE
клаузата изобщо няма да работи. -
TABLE tbl;
е основно съкращение отSELECT * FROM tbl;
. -
Подробности за
format()
в ръководството.