PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Utiliser la sortie texte d'une fonction comme nouvelle requête

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.