Din funktion kunne se sådan ud:
CREATE FUNCTION select_transactions3(_col text, _val text, _limit int)
RETURNS SETOF transactions AS
$BODY$
BEGIN
RETURN QUERY EXECUTE '
SELECT *
FROM transactions
WHERE ' || quote_ident(_col) || ' = $1
LIMIT $2'
USING _val, _limit;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
I PostgreSQL 9.1 eller senere, det er enklere med format()
...
RETURN QUERY EXECUTE format('
SELECT *
FROM transactions
WHERE %I = $1
LIMIT $2', _col)
USING _val, _limit;
...
%I
undslipper identifikatorer som quote_ident()
.
Vigtige punkter:
-
Du stødte ind i begrænsningen af dynamisk SQL, at du ikke kan bruge parametre til identifikatorer. Du skal bygge forespørgselsstrengen med kolonnenavnet og derefter udføre det.
-
Du kan dog gøre det med værdier. Jeg demonstrerer brugen af
USING
klausul forEXECUTE
. Bemærk også brugen af quote_ident()
:forhindrer SQL-injektion og visse syntaksfejl. -
Jeg har også stort set forenklet din funktion.
[RETURN QUERY EXECUTE][3]
gør din kode kortere og hurtigere. Du behøver ikke at gå i løkke, hvis alt du gør er at returnere rækken. -
Jeg bruger navnet
IN
parametre, så du ikke bliver forvekslet med $-notationen i forespørgselsstrengen.$1
og$2
inde i forespørgselsstrengen henvises til værdierne iUSING
klausul, ikke til inputparametrene. -
Jeg skifter til
SELECT *
da du alligevel skal returnere hele rækken for at matche den deklarerede returtype. -
Sidst men ikke mindst:Sørg for at overveje, hvad manualen har at sige om funktioner, der er erklæret
SECURITY DEFINER
.
RETURNERINGSTYPE
Hvis du ikke ønsker at returnere hele rækken, er en praktisk mulighed:
CREATE FUNCTION select_transactions3(_col text, _val text, _limit int)
RETURNS TABLE (invoice_no varchar(125), amount numeric(12,2) AS ...
Så behøver du ikke at angive en kolonnedefinitionsliste med hvert opkald og kan forenkle til:
SELECT * FROM select_to_transactions3('invoice_no', '1103300105472', 1);