Práce s touto falešnou tabulkou
CREATE TEMP TABLE foo (id int, my_num numeric);
INSERT INTO foo VALUES (1, 12.34)
Nejprve jsem váš příklad zjednodušil a vyčistil:
-
Byl odstraněn nějaký šum, který není pro otázku relevantní.
-
RETURNS SETOF void
sotva dává smysl. PoužívámRETURNS void
místo toho. -
Používám
text
místocharacter varying
, jen pro jednoduchost. -
Při použití dynamického SQL máte pro ochranu před SQL injection používám
format()
s%I
v tomto případě. Existují i jiné způsoby.
Základní problém je, že SQL je velmi rigidní s typy a identifikátory. Pracujete s dynamickou tabulkou název a také název dynamického pole záznamu - anonymní záznam ve vašem původním příkladu. Pl/pgSQL není na to dobře vybaven. Postgres neví, co je uvnitř anonymní záznam. Teprve poté, co záznam přiřadíte k dobře známému typu můžete odkazovat na jednotlivá pole.
Zde je úzce související otázka, kterou se snažíte nastavit pole záznamu s dynamickým názvem:
Jak nastavit hodnotu pole složené proměnné pomocí dynamického SQL
Základní funkce
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;
Volejte:
SELECT * from getrowdata1('foo', 1);
Komentovaná část by vyvolala výjimku:
nemohl identifikovat sloupec "my_num" v datovém typu záznamu:SELECT * fromgetrowdata(1,'foo')
hstore
Je třeba nainstalovat přídavný modul hstore pro tohle. Jednou za databázi s:
CREATE EXTENSION hstore;
Pak by vše mohlo fungovat takto:
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;
Volejte:
SELECT * from getrowdata2('foo', 1);
Polymorfní typ
Alternativa bez instalace dalších modulů.
Protože do proměnné záznamu vyberete celý řádek, existuje dobře definovaný typ pro to podle definice. Použij to. Klíčovým slovem jsou polymorfní typy .
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;
Volejte:
SELECT * from getrowdata3(NULL::foo, 1);
-> SQLfiddle
-
(ab-)používám vstupní parametr
_tbl
za tři účely zde:- Poskytuje dobře definovaný typ záznamu
- Poskytuje jméno tabulky, automaticky kvalifikováno podle schématu
- Slouží jako proměnná.
-
Další vysvětlení v této související odpovědi (poslední kapitola):
Refaktorujte funkci PL/pgSQL tak, aby vrátila výstup různých dotazů SELECT