Praca z tym atrapą stołu
CREATE TEMP TABLE foo (id int, my_num numeric);
INSERT INTO foo VALUES (1, 12.34)
Najpierw uprościłem i oczyściłem twój przykład:
-
Usunięto szum, który nie ma związku z pytaniem.
-
RETURNS SETOF void
prawie nie ma sensu. UżywamRETURNS void
zamiast tego. -
Używam
text
zamiastcharacter varying
, tylko ze względu na prostotę. -
Używając dynamicznego SQL, masz aby zabezpieczyć się przed wstrzyknięciem SQL, używam
format()
z%I
w tym przypadku. Są inne sposoby.
Podstawowym problemem jest to, że SQL jest bardzo sztywny pod względem typów i identyfikatorów. Korzystasz z tabeli dynamicznej nazwę oraz nazwę pola dynamicznego rekordu - anonimowy nagraj w twoim oryginalnym przykładzie. Pl/pgSQL nie jest dobrze przygotowany do radzenia sobie z tym problemem. Postgres nie wie, co jest w środku anonimowy rekord. Dopiero po przypisaniu rekordu do dobrze znanego typu czy możesz odwołać się do poszczególnych pól.
Oto ściśle powiązane pytanie, próbując ustawić pole rekordu z dynamiczną nazwą:
Jak ustawić wartość pola zmiennej złożonej za pomocą dynamicznego SQL
Funkcja podstawowa
CREATE OR REPLACE FUNCTION getrowdata1(table_name text, id int)
RETURNS void AS
$func$
DECLARE
srowdata record;
reqfield text := 'my_num'; -- assigning at declaration time for convenience
value numeric;
BEGIN
RAISE NOTICE 'id: %', id;
EXECUTE format('SELECT * FROM %I WHERE id = $1', table_name)
USING id
INTO srowdata;
RAISE NOTICE 'srowdata: %', srowdata;
RAISE NOTICE 'srowdatadata.my_num: %', srowdata.my_num;
/* This does not work, even with dynamic SQL
EXECUTE format('SELECT ($1).%I', reqfield)
USING srowdata
INTO value;
RAISE NOTICE 'value: %', value;
*/
END
$func$ LANGUAGE plpgsql;
Zadzwoń:
SELECT * from getrowdata1('foo', 1);
Komentowana część wywołałaby wyjątek:
nie można zidentyfikować kolumny "my_num" w typie danych rekordu:SELECT * fromgetrowdata(1,'foo')
hstore
Musisz zainstalować dodatkowy moduł hstore dla tego. Raz na bazę danych z:
CREATE EXTENSION hstore;
Wtedy wszystko mogłoby działać tak:
CREATE OR REPLACE FUNCTION getrowdata2(table_name text, id int)
RETURNS void AS
$func$
DECLARE
hstoredata hstore;
reqfield text := 'my_num';
value numeric;
BEGIN
RAISE NOTICE 'id: %', id;
EXECUTE format('SELECT hstore(t) FROM %I t WHERE id = $1', table_name)
USING id
INTO hstoredata;
RAISE NOTICE 'hstoredata: %', hstoredata;
RAISE NOTICE 'hstoredata.my_num: %', hstoredata -> 'my_num';
value := hstoredata -> reqfield;
RAISE NOTICE 'value: %', value;
END
$func$ LANGUAGE plpgsql;
Zadzwoń:
SELECT * from getrowdata2('foo', 1);
Typ polimorficzny
Alternatywa bez instalowania dodatkowych modułów.
Ponieważ wybierasz cały wiersz do zmiennej rekordu, istnieje dobrze zdefiniowany typ za to z definicji. Użyj tego. Słowo kluczowe to typy polimorficzne .
CREATE OR REPLACE FUNCTION getrowdata3(_tbl anyelement, id int)
RETURNS void AS
$func$
DECLARE
reqfield text := 'my_num';
value numeric;
BEGIN
RAISE NOTICE 'id: %', id;
EXECUTE format('SELECT * FROM %s WHERE id = $1', pg_typeof(_tbl))
USING id
INTO _tbl;
RAISE NOTICE '_tbl: %', _tbl;
RAISE NOTICE '_tbl.my_num: %', _tbl.my_num;
EXECUTE 'SELECT ($1).' || reqfield -- requfield must be SQLi-safe or escape
USING _tbl
INTO value;
RAISE NOTICE 'value: %', value;
END
$func$ LANGUAGE plpgsql;
Zadzwoń:
SELECT * from getrowdata3(NULL::foo, 1);
-> SQLfiddle
-
Używam (ab-) parametru wejściowego
_tbl
dla trzech cele tutaj:- Zapewnia dobrze zdefiniowany typ rekordu
- Zapewnia nazwę tabeli, automatycznie kwalifikowana według schematu
- Służy jako zmienna.
-
Więcej wyjaśnień w tej powiązanej odpowiedzi (ostatni rozdział):
Refaktoryzuj funkcję PL/pgSQL, aby zwrócić dane wyjściowe różnych zapytań SELECT