Dette kan forenkles og forbedres yderligere:
CREATE OR REPLACE FUNCTION some_f(_tbl regclass, OUT result integer)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('SELECT (EXISTS (SELECT FROM %s WHERE id = 1))::int', _tbl)
INTO result;
END
$func$;
Ring med skema-kvalificeret navn (se nedenfor):
SELECT some_f('myschema.mytable'); -- would fail with quote_ident()
Eller:
SELECT some_f('"my very uncommon table name"');
Vigtige punkter
Brug en OUT
parameter for at forenkle funktionen. Du kan direkte vælge resultatet af den dynamiske SQL ind i det og være færdig. Intet behov for yderligere variabler og kode.
EXISTS
gør præcis hvad du vil. Du får true
hvis rækken eksisterer eller false
Ellers. Der er forskellige måder at gøre dette på, EXISTS
er typisk mest effektiv.
Du ser ud til at have et heltal tilbage, så jeg kaster boolean
resultat fra EXISTS
til integer
, hvilket giver præcis det, du havde. Jeg ville returnere boolesk i stedet.
Jeg bruger objektidentifikatortypen regclass
som inputtype for _tbl
. Det gør alt quote_ident(_tbl)
eller format('%I', _tbl)
ville gøre det, men bedre, fordi:
-
.. det forhindrer SQL-injektion lige så godt.
-
.. det mislykkes med det samme og mere yndefuldt, hvis tabelnavnet er ugyldigt / ikke eksisterer / er usynligt for den aktuelle bruger. (En
regclass
parameter gælder kun for eksisterende tabeller.) -
.. det virker med skema-kvalificerede tabelnavne, hvor en almindelig
quote_ident(_tbl)
ellerformat(%I)
ville mislykkes, fordi de ikke kan løse tvetydigheden. Du skal sende og escape skema- og tabelnavne separat.
Det virker kun for eksisterende tabeller, naturligvis.
Jeg bruger stadig format()
, fordi det forenkler syntaksen (og for at demonstrere, hvordan det bruges), men med %s
i stedet for %I
. Typisk er forespørgsler mere komplekse, så format()
hjælper mere. For det simple eksempel kunne vi lige så godt bare sammenkæde:
EXECUTE 'SELECT (EXISTS (SELECT FROM ' || _tbl || ' WHERE id = 1))::int'
Ingen grund til at tabelkvalificere id
kolonne, mens der kun er en enkelt tabel i FROM
liste. Ingen tvetydighed mulig i dette eksempel. (Dynamisk) SQL-kommandoer inde i EXECUTE
have et separat omfang , funktionsvariabler eller parametre er ikke synlige der - i modsætning til almindelige SQL-kommandoer i funktionsteksten.
Her er grunden til, at du altid escape brugerinput til dynamisk SQL korrekt:
db<>spil her demonstrerer SQL-injektion
Gamle sqlfiddle