Ce système peut encore être simplifié et amélioré :
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$;
Appel avec un nom qualifié de schéma (voir ci-dessous) :
SELECT some_f('myschema.mytable'); -- would fail with quote_ident()
Ou :
SELECT some_f('"my very uncommon table name"');
Principaux points
Utilisez un OUT
paramètre pour simplifier la fonction. Vous pouvez y sélectionner directement le résultat du SQL dynamique et le tour est joué. Pas besoin de variables et de code supplémentaires.
EXISTS
fait exactement ce que vous voulez. Vous obtenez true
si la ligne existe ou false
autrement. Il existe plusieurs façons de procéder, EXISTS
est généralement la plus efficace.
Vous semblez vouloir un entier de retour, alors j'ai lancé le boolean
résultat de EXISTS
a integer
ce qui donne exactement ce que vous aviez. Je retournerais boolean à la place.
J'utilise le type d'identifiant d'objet regclass
comme type d'entrée pour _tbl
. Cela fait tout quote_ident(_tbl)
o format('%I', _tbl)
ferait l'affaire, mais en mieux, car :
-
il empêche Injection SQL tout aussi bien.
-
il échoue immédiatement et plus gracieusement si le nom de la table est invalide / n'existe pas / est invisible pour l'utilisateur actuel. (A regclass
est uniquement applicable pour existant tables.)
-
il fonctionne avec des noms de table qualifiés de schéma, où un simple quote_ident(_tbl)
o format(%I)
échoueraient parce qu'ils ne peuvent pas résoudre l'ambiguïté. Vous devez transmettre et échapper les noms de schémas et de tables séparément.
Il ne fonctionne que pour existant des tables, évidemment.
J'utilise toujours format()
parce que cela simplifie la syntaxe (et pour montrer comment l'utiliser), mais avec %s
au lieu de %I
. En général, les requêtes sont plus complexes, donc format()
aide plus. Pour l'exemple simple, nous pourrions tout aussi bien concaténer :
EXECUTE 'SELECT (EXISTS (SELECT FROM ' || _tbl || ' WHERE id = 1))::int'
Il n'est pas nécessaire de qualifier la table id
alors qu'il n'y a qu'une seule table dans le fichier FROM
liste. Aucune ambiguïté possible dans cet exemple. Commandes SQL (dynamiques) à l'intérieur EXECUTE
ont un champ d'application distinct Les variables ou les paramètres de la fonction ne sont pas visibles à cet endroit, contrairement aux commandes SQL simples dans le corps de la fonction.
Voici pourquoi vous toujours échapper correctement à l'entrée utilisateur pour le SQL dynamique :
_db<>fidèle aquí_ Démonstration de l'injection SQL
Vieux <a href="http://sqlfiddle.com/#!17/39ef7/15" rel="noreferrer">sqlfiddle</a>