Trik s PREPARE
nefunguje, protože nebere * textový řetězec* (hodnotu) jako CREATE FUNCTION
ano, ale platné prohlášení (kód).
Chcete-li převést data do spustitelného kódu musíte použít dynamické SQL, tj. EXECUTE
ve funkci plpgsql nebo DO
prohlášení. Toto funguje bez problémů, pokud návratový typ nezávisí na výsledku první funkce myresult()
. Jinak jste zpět a chytíte 22, jak je uvedeno v mé předchozí odpovědi:
- Jak spustit výsledek řetězce uložené procedury v postgres
Rozhodující částí je deklarovat typ návratu (v tomto případě typ řádku) nějak. Můžete vytvořit TABLE
, TEMP TABLE
nebo TYPE
za účelem. Nebo můžete použít připravený příkaz nebo rekurzor.
Řešení s připraveným prohlášením
Byli jste si velmi blízcí. Chybějícím kouskem skládačky je připravit vygenerovaný dotaz pomocí dynamického SQL .
Funkce pro dynamickou přípravu příkazu
Vytvořte tuto funkci jednou . Je to optimalizovaná a bezpečná verze vaší funkce myresult()
:
CREATE OR REPLACE FUNCTION f_prep_query (_tbl regclass, _prefix text)
RETURNS void AS
$func$
BEGIN
IF EXISTS (SELECT 1 FROM pg_prepared_statements WHERE name = 'stmt_dyn') THEN
DEALLOCATE stmt_dyn;
END IF; -- you my or may not need this safety check
EXECUTE (
SELECT 'PREPARE stmt_dyn AS SELECT '
|| string_agg(quote_ident(attname), ',' ORDER BY attname)
|| ' FROM ' || _tbl
FROM pg_catalog.pg_attribute
WHERE attrelid = _tbl
AND attname LIKE _prefix || '%'
AND attnum > 0
AND NOT attisdropped
);
END
$func$ LANGUAGE plpgsql;
Používám regclass
pro parametr názvu tabulky _tbl
aby to bylo jednoznačné a bezpečné proti SQLi. Podrobnosti:
- Název tabulky jako parametr funkce PostgreSQL
Informační schéma nezahrnuje sloupec oid systémových katalogů, takže jsem přešel na pg_catalog.pg_attribute
namísto information_schema.columns
. To je taky rychlejší. To má své klady a zápory:
- Jak zkontrolovat, zda tabulka v daném schématu existuje
Pokud je připravený výpis s názvem stmt_dyn
již existuje, PREPARE
vyvolá výjimku. Pokud je to přijatelné, zrušte zaškrtnutí v systémovém pohledu pg_prepared_statements
a následující DEALLOCATE
.
Sofistikovanější algoritmy je možné spravovat více připravených příkazů na relaci, nebo vzít název připraveného příkazu jako další parametr, nebo dokonce použít MD5 hash řetězce dotazu jako název, ale to je nad rámec rozsah této otázky.
Uvědomte si, že PREPARE
působí mimo rozsah transakcí , jednou PREPARE
uspěje, připravený příkaz existuje po celou dobu trvání relace. Pokud je transakce balení přerušena, PREPARE
je nedotčena. ROLLBACK
nelze odstranit připravené výpisy.
Dynamické provádění dotazu
Dva dotazy, ale pouze jeden volání na server. A také velmi efektivní.
SELECT f_prep_query('tbl'::regclass, 'pre'::text);
EXECUTE stmt_dyn;
Jednodušší a mnohem efektivnější pro většinu jednoduchých případů použití než vytvoření dočasné tabulky nebo kurzoru a výběr/načítání z nich (což by byly další možnosti).
SQL Fiddle.