web-dev-qa-db-fra.com

Manière élégante de gérer les exceptions PostgreSQL?

Dans PostgreSQL, je voudrais créer un mécanisme de wrapping sécurisé qui renvoie un résultat vide si une exception se produit. Considérer ce qui suit:

SELECT * FROM myschema.mytable;

Je pourrais faire le wrapping en toute sécurité dans l'application cliente:

try {
    result = execute_query('SELECT value FROM myschema.mytable').fetchall();
}
catch(pg_exception) {
    result = []
}

Mais pourrais-je faire une telle chose directement dans SQL? Je voudrais faire fonctionner le code suivant, mais il semble qu'il devrait être mis en bloc DO $$ ... $$ et ici je me perds.

BEGIN
    SELECT * FROM myschema.mytable;
EXCEPTION WHEN others THEN
    SELECT unnest(ARRAY[]::TEXT[])
END
13
Tregoreg

Gestion des exceptions dans PL/pgSQL

Généralement, le code plpgsql est toujours encapsulé dans un bloc BEGIN .. END. Cela peut être à l'intérieur du corps d'une instruction DO ou d'une fonction. Les blocs peuvent être imbriqués à l'intérieur - mais ils ne peuvent pas exister en dehors de, ne confondez pas ceci avec du SQL pur.

Chaque bloc BEGIN peut éventuellement inclure une clause EXCEPTION pour gérer les exceptions, mais les fonctions devant intercepter des exceptions sont considérablement plus coûteuses. Il est donc préférable d'éviter les exceptions a priori.

Plus d'information:

Comment éviter une exception dans l'exemple

Une instruction DO ne peut rien retourner. Créez une fonction qui prend les noms de table et de schéma en tant que paramètres et vous renvoie tout ce que vous voulez:

CREATE OR REPLACE FUNCTION f_tbl_value(_tbl text, _schema text = 'public')
  RETURNS TABLE (value text) AS
$func$
DECLARE
   _t regclass := to_regclass(_schema || '.' || _tbl);
BEGIN
   IF _t IS NULL THEN
      value := ''; RETURN NEXT;    -- return single empty string
   ELSE
      RETURN QUERY EXECUTE
      'SELECT value FROM ' || _t;  -- return set of values
   END
$func$ LANGUAGE plpgsql;

Appel: 

SELECT * FROM f_tbl_value('my_table');

Ou:

SELECT * FROM f_tbl_value('my_table', 'my_schema');
  • En supposant que vous souhaitiez un ensemble de lignes avec une seule colonne text ou une chaîne vide si la table n'existe pas.

  • En supposant également qu’une colonne value existe si la table donnée existe. Vous pouvez également tester cela, mais vous ne l'avez pas demandé.

  • Les deux paramètres sont sensibles à la cassetext. C'est légèrement différent de la façon dont les identifiants dans les instructions SQL sont gérés . Si vous ne doublez jamais les identificateurs, transmettez les noms en minuscules et tout va bien.

  • Le nom de schéma par défaut est 'public' dans mon exemple. Adaptez-vous à vos besoins. Vous pouvez même ignorer complètement le schéma et utiliser par défaut le search_path actuel.

  • to_regclass() est nouvelle dans Postgres 9.4. Pour les versions plus anciennes, remplacez:

    IF EXISTS (
       SELECT 1
       FROM   information_schema.tables 
       WHERE  table_schema = _schema
       AND    table_name = _tbl
    );
    

    Ceci est en fait plus précis, car il teste exactement ce dont vous avez besoin ..__ Plus d'options et une explication détaillée:

  • Défendez-vous toujours contre l'injection SQL lorsque vous travaillez avec du SQL dynamique! Le casting de regclass fait le tour ici. Plus de détails:

10
Erwin Brandstetter

Si vous ne sélectionnez qu'une colonne, la fonction COALESCE () devrait pouvoir vous aider.

SELECT COALESCE( value, '{}'::text[] ) FROM myschema.mytable

Si vous avez besoin de plus de lignes, vous devrez peut-être créer une fonction avec des types.

0
Lucas