L'astuce avec PREPARE
ne fonctionne pas, car il ne prend pas une * chaîne de texte * (une valeur) comme CREATE FUNCTION
le fait, mais une instruction valide (code).
Pour convertir des données en code exécutable vous devez utiliser SQL dynamique, c'est-à-dire EXECUTE
dans une fonction plpgsql ou DO
déclaration. Cela fonctionne sans problème tant que le type de retour ne dépend pas du résultat de la première fonction myresult()
. Sinon, vous êtes de retour pour attraper 22 comme indiqué dans ma réponse précédente :
- Comment exécuter un résultat de chaîne d'une procédure stockée dans postgres
La partie cruciale est de déclarer le type de retour (type de ligne dans ce cas) en quelque sorte. Vous pouvez créer une TABLE
, TEMP TABLE
ou TYPE
aux fins. Ou vous pouvez utiliser une instruction préparée ou un refcursor.
Solution avec déclaration préparée
Vous avez été très proches. La pièce manquante du puzzle est de préparer la requête générée avec SQL dynamique .
Fonction de préparation dynamique du relevé
Créez cette fonction une fois . C'est une version optimisée et sûre de votre fonction 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;
J'utilise regclass
pour le paramètre de nom de table _tbl
pour le rendre sans ambiguïté et sûr contre SQLi. Détails :
- Nom de table en tant que paramètre de fonction PostgreSQL
Le schéma d'information n'inclut pas la colonne oid des catalogues système, donc je suis passé à pg_catalog.pg_attribute
au lieu de information_schema.columns
. C'est aussi plus rapide. Il y a des avantages et des inconvénients à cela :
- Comment vérifier si une table existe dans un schéma donné
Si une instruction préparée avec le nom stmt_dyn
existait déjà, PREPARE
lèverait une exception. Si cela est acceptable, décochez la vue système pg_prepared_statements
et le DEALLOCATE
suivant .
Des algorithmes plus sophistiqués sont possibles pour gérer plusieurs instructions préparées par session, ou prendre le nom de l'instruction préparée comme paramètre supplémentaire, ou même utiliser un hachage MD5 de la chaîne de requête comme nom, mais cela va au-delà du portée de cette question.
Sachez que PREPARE
opère en dehors du cadre des transactions , une fois PREPARE
réussit, l'instruction préparée existe pour la durée de vie de la session. Si la transaction d'emballage est abandonnée, PREPARE
n'est pas affecté. ROLLBACK
ne peut pas supprimer les instructions préparées.
Exécution dynamique des requêtes
Deux requêtes, mais seulement une appel au serveur. Et très efficace aussi.
SELECT f_prep_query('tbl'::regclass, 'pre'::text);
EXECUTE stmt_dyn;
Plus simple et beaucoup plus efficace pour la plupart des cas d'utilisation simples que de créer une table temporaire ou un curseur et de sélectionner/récupérer à partir de cela (ce qui serait d'autres options).
SQL Fiddle.