Twoja funkcja może wyglądać tak:
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;
W PostgreSQL 9.1 lub później jest to prostsze z formatem format()
...
RETURN QUERY EXECUTE format('
SELECT *
FROM transactions
WHERE %I = $1
LIMIT $2', _col)
USING _val, _limit;
...
%I
unika identyfikatorów, takich jak quote_ident()
.
Główne punkty:
-
Wpadłeś na ograniczenie dynamicznego SQL, polegające na tym, że nie możesz używać parametrów dla identyfikatorów. Musisz zbudować ciąg zapytania z nazwą kolumny i następnie wykonaj go.
-
Możesz to zrobić z wartościami. Demonstruję użycie
USING
klauzulaEXECUTE
. Zwróć także uwagę na użyciequote_ident()
:zapobiega wstrzykiwaniu SQL i pewnym błędom składni. -
W dużym stopniu uprościłem też twoją funkcję.
[RETURN QUERY EXECUTE][3]
sprawia, że Twój kod jest krótszy i szybszy. Nie ma potrzeby zapętlania się, jeśli wszystko, co robisz, to zwracanie wiersza. -
Używam nazwy
IN
parametrów, dzięki czemu nie pomylisz się z notacją $ w ciągu zapytania.$1
i$2
wewnątrz ciągu zapytania odnieś się do wartości podanych wUSING
klauzuli, a nie do parametrów wejściowych. -
Zmieniam na
SELECT *
ponieważ i tak musisz zwrócić cały wiersz, aby pasował do zadeklarowanego typu zwracanego. -
Last but not least:pamiętaj, aby zastanowić się, co podręcznik ma do powiedzenia na temat funkcji zadeklarowanych
SECURITY DEFINER
.
TYP ZWROTU
Jeśli nie chcesz zwracać całego wiersza, jedną dogodną możliwością jest:
CREATE FUNCTION select_transactions3(_col text, _val text, _limit int)
RETURNS TABLE (invoice_no varchar(125), amount numeric(12,2) AS ...
Wtedy nie musisz dostarczać listy definicji kolumn przy każdym wywołaniu i możesz uprościć:
SELECT * FROM select_to_transactions3('invoice_no', '1103300105472', 1);