web-dev-qa-db-fra.com

Argument facultatif dans la fonction PL / pgSQL

J'essaie d'écrire une fonction PL/pgSQL avec des arguments facultatifs. Il exécute une requête basée sur un ensemble filtré d'enregistrements (si spécifié), sinon, il exécute une requête sur l'ensemble de données dans une table.

Par exemple (CODE PSEUDO):

CREATE OR REPLACE FUNCTION foofunc(param1 integer, param2 date, param2 date, optional_list_of_ids=[]) RETURNS SETOF RECORD AS $$
    IF len(optional_list_of_ids) > 0 THEN
        RETURN QUERY (SELECT * from foobar where f1=param1 AND f2=param2 AND id in optional_list_of_ids);
    ELSE
        RETURN QUERY (SELECT * from foobar where f1=param1 AND f2=param2);
    ENDIF
$$ LANGUAGE SQL;

Quelle serait la bonne façon de mettre en œuvre cette fonction?

En passant, je voudrais savoir comment je pourrais appeler une telle fonction dans une autre fonction extérieure. C'est ainsi que je le ferais - est-ce correct ou existe-t-il une meilleure façon?

CREATE FUNCTION foofuncwrapper(param1 integer, param2 date, param2 date) RETURNS SETOF RECORD AS $$
BEGIN
   CREATE TABLE ids AS SELECT id from foobar where id < 100;
   RETURN QUERY (SELECT * FROM foofunc(param1, param2, ids));
END
$$ LANGUAGE SQL
29

Depuis PostgreSQL 8.4 (que vous semblez exécuter), il existe valeurs par défaut pour les paramètres de fonction . Si vous placez votre paramètre en dernier et fournissez une valeur par défaut, vous pouvez simplement l'omettre de l'appel:

CREATE OR REPLACE FUNCTION foofunc(_param1 integer
                                 , _param2 date
                                 , _ids    int[] DEFAULT '{}')
  RETURNS SETOF foobar AS -- declare return type!
$func$
BEGIN  -- required for plpgsql
   IF _ids <> '{}'::int[] THEN -- exclude empty array and NULL
      RETURN QUERY
      SELECT *
      FROM   foobar
      WHERE  f1 = _param1
      AND    f2 = _param2
      AND    id = ANY(_ids); -- "IN" is not proper syntax for arrays
   ELSE
      RETURN QUERY
      SELECT *
      FROM   foobar
      WHERE  f1 = _param1
      AND    f2 = _param2;
   END IF;
END  -- required for plpgsql
$func$  LANGUAGE plpgsql;

Points majeurs:

  • Le mot clé DEFAULT est utilisé pour déclarer les paramètres par défaut. Alternative courte: =.

  • J'ai supprimé le redondant param1 de l'exemple désordonné.

  • Depuis que vous revenez SELECT * FROM foobar, déclarez le type de retour comme RETURNS SETOF foobar au lieu de RETURNS SETOF record. Ce dernier formulaire avec des enregistrements anonymes est très compliqué, vous devez fournir une liste de définition de colonne à chaque appel.

  • J'utilise un tableau d'entiers (int[]) comme paramètre de fonction. Adapté l'expression IF et la clause WHERE en conséquence.

  • Les instructions IF ne sont pas disponibles en SQL simple. Doit être LANGUAGE plpgsql pour ça.

Appelez avec ou sans _ids:

SELECT * FROM foofunc(1, '2012-1-1'::date);

Effectivement les mêmes:

SELECT * FROM foofunc(1, '2012-1-1'::date, '{}'::int[]);

Vous devez vous assurer que l'appel est sans ambiguïté. Si vous avez une autre fonction du même nom et deux paramètres, Postgres peut ne pas savoir lequel choisir. Le casting explicite (comme je le démontre) le réduit. Sinon, les littéraux de chaîne non typés fonctionnent aussi, mais être explicite ne fait jamais de mal.

Appel depuis une autre fonction:

CREATE FUNCTION foofuncwrapper(_param1 integer, _param2 date)
  RETURNS SETOF foobar AS
$func$
DECLARE
   _ids int[] := '{1,2,3}';
BEGIN
   -- irrelevant stuff

   RETURN QUERY
   SELECT * FROM foofunc(_param1, _param2, _ids);
END
$func$  LANGUAGE plgpsql;
42
Erwin Brandstetter

Élaborer sur la réponse de Frank sur ce fil:

L'argument VARIADIC ne doit pas être le seul argument, seulement le dernier.

Vous pouvez utiliser VARIADIC pour les fonctions qui peuvent prendre zéro argument variadique, c'est juste un peu plus compliqué en ce qu'il nécessite un style d'appel différent pour zéro args. Vous pouvez fournir une fonction wrapper pour masquer la laideur. Étant donné une définition initiale de la fonction varardique comme:

CREATE OR REPLACE FUNCTION foofunc(param1 integer, param2 date, param2 date, optional_list_of_ids VARIADIC integer[]) RETURNS SETOF RECORD AS $$
....
$$ language sql;

Pour zéro argument, utilisez un wrapper comme:

CREATE OR REPLACE FUNCTION foofunc(integer, date, date) RETURNS SETOF RECORD AS $body$
SELECT foofunc($1,$2,$3,VARIADIC ARRAY[]::integer[]);
$body$ LANGUAGE 'sql';

ou appelez simplement la fonction principale avec un tableau vide comme VARIADIC '{}'::integer[] directement. L'emballage est moche, mais il est contenu laid, donc je recommanderais d'utiliser un emballage.

Les appels directs peuvent être effectués sous forme variadique:

SELECT foofunc(1,'2011-01-01','2011-01-01', 1, 2, 3, 4);

... ou forme d'appel tableau avec tableau ctor:

SELECT foofunc(1,'2011-01-01','2011-01-01', VARIADIC ARRAY[1,2,3,4]);

... ou forme littérale du texte du tableau:

SELECT foofunc(1,'2011-01-01','2011-01-01', VARIADIC '{1,2,3,4}'::int[]);

Les deux dernières formes fonctionnent avec des tableaux vides.

3
Craig Ringer

Vous voulez dire Fonctions SQL avec un nombre variable d'arguments ? Si c'est le cas, utilisez VARIADIC.

1
Frank Heikens