De truc met PREPARE
werkt niet, omdat er geen * tekststring* (een waarde) voor nodig is, zoals CREATE FUNCTION
doet, maar een geldige verklaring (code).
gegevens converteren in uitvoerbare code je moet dynamische SQL gebruiken, d.w.z. EXECUTE
in een plpgsql-functie of DO
uitspraak. Dit werkt probleemloos zolang het retourtype niet afhangt van de uitkomst van de eerste functie myresult()
. Anders ben je terug om 22 te vangen, zoals uiteengezet in mijn vorige antwoord:
- Hoe een stringresultaat van een opgeslagen procedure in postgres uit te voeren
Het cruciale onderdeel is om het retourtype te declareren (rijtype in dit geval) op de een of andere manier. U kunt een TABLE
. maken , TEMP TABLE
of TYPE
Voor het doel. Of u kunt een voorbereide verklaring of een refcursor gebruiken.
Oplossing met voorbereide verklaring
Je bent heel dichtbij geweest. Het ontbrekende stukje van de puzzel is om de gegenereerde query voor te bereiden met dynamische SQL .
Functie om verklaring dynamisch voor te bereiden
Maak deze functie eenmaal . Het is een geoptimaliseerde en veilige versie van uw functie 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;
Ik gebruik regclass
voor de tabelnaamparameter _tbl
om het ondubbelzinnig en veilig te maken tegen SQLi. Details:
- Tabelnaam als een PostgreSQL-functieparameter
Het informatieschema bevat niet de oid-kolom van systeemcatalogi, dus schakelde ik over naar pg_catalog.pg_attribute
in plaats van information_schema.columns
. Dat is ook sneller. Hier zijn voor- en nadelen aan:
- Hoe te controleren of een tabel in een bepaald schema bestaat
Als een voorbereide verklaring met de naam stmt_dyn
bestond al, PREPARE
een uitzondering zou maken. Als dat acceptabel is, verwijder dan het vinkje in de systeemweergave pg_prepared_statements
en de volgende DEALLOCATE
.
Er zijn meer geavanceerde algoritmen mogelijk om meerdere voorbereide instructies per sessie te beheren, of de naam van de voorbereide instructie als extra parameter te nemen, of zelfs een MD5-hash van de queryreeks als naam te gebruiken, maar dat gaat verder dan de reikwijdte van deze vraag.
Houd er rekening mee dat PREPARE
opereert buiten het bereik van transacties , eenmaal PREPARE
slaagt, bestaat de voorbereide verklaring voor de levensduur van de sessie. Als de inpaktransactie wordt afgebroken, PREPARE
is onaangetast. ROLLBACK
kan niet verwijder voorbereide verklaringen.
Dynamische query-uitvoering
Twee zoekopdrachten, maar slechts één naar de server bellen. En ook nog eens erg efficiënt.
SELECT f_prep_query('tbl'::regclass, 'pre'::text);
EXECUTE stmt_dyn;
Eenvoudiger en veel efficiënter voor de meeste eenvoudige gebruiksgevallen dan het maken van een tijdelijke tabel of een cursor en daaruit selecteren / ophalen (wat andere opties zouden zijn).
SQL Fiddle.