PostgreSQLLa base de données la plus sophistiquée au monde.

41. PL/Python - Langage de procédures Python

Le langage de procédures PL/Python permet l'écriture de fonctions PostgreSQL™ avec le langage Python.

Pour installer PL/Python dans une base de données particulières, utilisez createlang plpythonu nom_base.

[Astuce]

Astuce

Si un langage est installé dans template1, toutes les bases nouvellement créées se verront installées ce langage automatiquement.

Depuis PostgreSQL™ 7.4, PL/Python est seulement disponible en tant que langage « sans confiance » (ceci signifiant qu'il n'offre aucun moyen de restreindre ce que les utilisateurs en font). Il a donc été renommé en plpythonu. La variante de confiance plpython pourrait être de nouveau disponible dans le futur, si un nouveau mécanisme sécurisé d'exécution est développé dans Python.

[Note]

Note

Les utilisateurs des paquets sources doivent activer spécifiquement la construction de PL/Python lors des étapes d'installation (référez-vous aux instructions d'installation pour plus d'informations). Les utilisateurs de paquets binaires pourront trouver PL/Python dans un paquet séparé.

41.1. Fonctions PL/Python

Les fonctions PL/Python sont déclarées via la syntaxe standard CREATE FUNCTION :

CREATE FUNCTION nom_fonction (liste-arguments)
  RETURNS return-type
AS $$
  # corps de la fonction PL/Python
$$ LANGUAGE plpythonu;

Le corps d'une fonction est tout simplement un script Python. Quand la fonction est appelée, ses arguments sont passés au script Python comme des éléments du tableau args[] ; les arguments nommés sont en plus passés comme des variables ordinaires. Le résultat est renvoyé par le code Python de la façon habituelle, avec return ou yield (dans le cas d'une instruction avec un ensemble de résultats).

Par exemple, une fonction renvoyant le plus grand de deux entiers peut être définie ainsi :

CREATE FUNCTION pymax (a integer, b integer)
  RETURNS integer
AS $$
  if a > b:
    return a
  return b
$$ LANGUAGE plpythonu;

Le code Python donné comme corps de la définition de fonction est transformé en fonction Python. Par exemple, le code ci-dessus devient :

def __plpython_procedure_pymax_23456():
  if a > b:
    return a
  return b

en supposant que 23456 est l'OID affecté à la fonction par PostgreSQL™.

Les paramètres de fonction PostgreSQL™ sont disponibles dans la liste globale args. Dans l'exemple pymax, args[0] contient tout ce qui a été passé comme premier argument et args[1] contient la valeur du deuxième argument. Sinon, vous pouvez utiliser les paramètres nommés comme le montre l'exemple ci-dessus. L'utilisation de paramètres nommés est habituellement plus lisible.

Si une valeur SQL NULL est passée à une fonction, la valeur de l'argument apparaîtra comme None au niveau de Python. La définition de la fonction ci-dessus renverra la mauvaise réponse pour des entrées NULL. Nous pouvons jouer STRICT à la définition de la fonction pour faire en sorte que PostgreSQL™ fasse quelque-chose de plus raisonnable : si une valeur NULL est passée, la fonction ne sera pas appelée du tout mais renverra juste un résultat NULL automatiquement. Sinon, vous pouver vérifier les entrées NULL dans le corps de la fonction :

CREATE FUNCTION pymax (a integer, b integer)
  RETURNS integer
AS $$
  if (a is None) or (b is None):
    return None
  if a > b:
    return a
  return b
$$ LANGUAGE plpythonu;

Comme montré ci-dessus, pour renvoyer une valeur SQL NULL à partir d'une fonction PL/Python, renvoyez la valeur None. Ceci peut se faire que la fonction soit stricte ou non.

Les arguments de type composite sont passés à la fonction via une correspondance Python. Les noms d'élément de la correspondance sont les noms d'attribut du type composite. Si un attribut a une valeur NULL dans la ligne traitée; il a la valeur NULL dans sa correspondance. Voici un exemple :

CREATE TABLE employe (
  nom text,
  salaire integer,
  age integer
);

CREATE FUNCTION trop_paye (e employe)
  RETURNS boolean
AS $$
  if e["salaire"] > 200000:
    return True
  if (e["age"] < 30) and (e["salaire"] > 100000):
    return True
  return False
$$ LANGUAGE plpythonu;

Il existe plusieurs façon de renvoyer une ligne ou des types composites à partir d'une fonction Python. Les exemples suivants supposent que nous avons :

CREATE TABLE valeur_nommee (
  nom   text,
  valeur  integer
);

ou

CREATE TYPE valeur_nommee AS (
  nom   text,
  valeur  integer
);

Une valeur composite peut être renvoyé comme :

Un type séquence (ligne ou liste), mais pas un ensemble parce que ce n'est pas indexable

Les objets séquences renvoyés doivent avoir le même nombre d'éléments que le type composite a de champs. L'élément d'index 0 est affecté au premier champ du type composite, 1 au second et ainsi de suite. Par exemple :

CREATE FUNCTION cree_paire (nom text, valeur integer)
  RETURNS valeur_nommee
AS $$
  return [ nom, valeur ]
  # ou autrement, en tant que ligne : return ( nom, valeur )
$$ LANGUAGE plpythonu;

Pour renvoyer NULL dans une colonne, insérez None à la position correspondante.

Correspondance (dictionnaire)

La valeur de chaque colonne du type résultat est récupérée à partir de la correspondance avec le nom de colonne comme clé. Exemple :

CREATE FUNCTION cree_paire (nom text, valeur integer)
  RETURNS valeur_nommee
AS $$
  return { "nom": nom, "valeur": valeur }
$$ LANGUAGE plpythonu;

Des paires clés/valeurs supplémentaires du dictionnaire sont ignorées. Les clés manquantes sont traitées comme des erreurs. Pour renvoyer NULL comme une colonne, insérez None avec le nom de la colonne correspondante comme clé.

Objet (tout objet fournissant la méthode __getattr__)

Ceci fonctionne de la même façon qu'une correspondance. Exemple :

CREATE FUNCTION cree_paire (nom text, valeur integer)
  RETURNS valeur_nommee
AS $$
  class valeur_nommee:
    def __init__ (self, n, v):
      self.nom = n
      self.valeur = v
  return valeur_nommee(nom, valeur)

  # ou simplement
  class nv: pass
  nv.nom = nom
  nv.valeur = valeur
  return nv
$$ LANGUAGE plpythonu;

Si vous ne fournissez pas de valeur de retour, PL/Python renvoie par défaut None. Le module du langage traduit le None de Python en la valeur NULL en SQL.

Une fonction PL/Python peut aussi renvoyer des ensembles scalaires ou des types composites. Il existe plusieurs façon de faire ceci parce que l'objet renvoyé est transformé en interne en itérateur. Les exemples suivants supposent que nous avons le type composite :

CREATE TYPE greeting AS (
  how text,
  who text
);

Un résultat ensemble peut être renvoyé à partir de :

Un type séquence (ligne, liste, ensemble)
CREATE FUNCTION greet (how text)
  RETURNS SETOF greeting
AS $$
  # renvoie la ligne contenant les listes en tant que types composites
  # toutes les autres combinaisons fonctionnent aussi
  return ( [ how, "World" ], [ how, "PostgreSQL" ], [ how, "PL/Python" ] )
$$ LANGUAGE plpythonu;
L'itérateur (tout objet fournissant les méthodes __iter__ et next)
CREATE FUNCTION greet (how text)
  RETURNS SETOF greeting
AS $$
  class producer:
    def __init__ (self, how, who):
      self.how = how
      self.who = who
      self.ndx = -1

    def __iter__ (self):
      return self

    def next (self):
      self.ndx += 1
      if self.ndx == len(self.who):
        raise StopIteration
      return ( self.how, self.who[self.ndx] )

  return producer(how, [ "World", "PostgreSQL", "PL/Python" ])
$$ LANGUAGE plpythonu;
Le générateur (yield)
CREATE FUNCTION greet (how text)
  RETURNS SETOF greeting
AS $$
  for who in [ "World", "PostgreSQL", "PL/Python" ]:
    yield ( how, who )
$$ LANGUAGE plpythonu;
[Avertissement]

Avertissement

Actuellement, à cause du bogue #1483133 de Python, certaines versions de débogage de Python 2.4 (configuré et compilé avec l'option --with-pydebug) sont connues pour arrêter brutalement le serveur PostgreSQL™ lors de l'utilisation d'un itérateur pour renvoyer un résultat ensemble. Les versions non corrigées de Fedora 4 contiennent ce bogue. Cela n'arrive pas dans les versions de production de Python et sur les versions corrigées de Fedora 4.

Le dictionnaire global SD est disponible pour stocker des données entres les appels de fonctions. Cette variable est une donnée statique privée. Le dictionnaire global GD est une donnée publique disponible pour toutes les fonctions Python à l'intérieur d'une session. À utiliser avec précaution.

Chaque fonction obtient son propre environnement d'exécution dans l'interpréteur Python, de façon à ce que les données globales et les arguments de fonction provenant de ma_fonction ne soient pas disponibles depuis ma_fonction2. L'exception concerne les données du dictionnaire GD comme indiqué ci-dessus.