Documentation PostgreSQL 9.1.24 > Programmation serveur > PL/Python - Langage de procédures Python > Accès à la base de données | |
Fonctions de déclencheurs | Sous-transactions explicites |
Le module du langage PL/Python importe automatiquement un module Python appelé plpy. Les fonctions et constantes de ce module vous sont accessibles dans le code Python via plpy.foo.
Le module plpy propose deux fonctions appelées execute et prepare. Appeler plpy.execute avec une requête sous forme de chaîne de caractères et un argument optionnel de limite fait que la requête est exécutée et le résultat renvoyé dans un objet résultat. Cet objet émule un objet liste ou dictionnaire. L'accès aux résultats se fait par numéro de ligne et nom de colonne. Deux méthodes supplémentaires sont utilisables : nrows qui renvoit le nombre de lignes renvoyées par la requête, et status qui correspond à la valeur de retour de SPI_execute(). L'objet résultat est modifiable.
Par exemple :
rv = plpy.execute("SELECT * FROM ma_table", 5)
renvoit jusqu'à cinq lignes de ma_table. Si ma_table a une colonne ma_colonne, son contenu peut être récupéré ainsi :
foo = rv[i]["ma_colonne"]
la seconde fonction, plpy.prepare, prépare le plan d'exécution d'une requête. Il utilise comme arguments une chaîne de caractères pour la requête et une liste des types de paramètres si des références de paramètres sont indiquées dans la requête. Par exemple :
plan = plpy.prepare("SELECT nom FROM mes_utilisateurs WHERE prenom = $1", [ "text" ])
text est le type de la variable que vous devrez passer pour $1. Après avoir préparé une requête, vous devez utiliser la fonction plpy.execute pour l'exécuter :
rv = plpy.execute(plan, [ "nom" ], 5)
Le troisième argument, optionnel, est la limite.
Les paramètres de requêtes et les champs de résultats sont convertis entre PostgreSQL et les types de données Python comme indiqué dans Section 42.3, « Valeur des données avec PL/Python ». L'exception est que les types composites ne sont pas actuellement supportés : ils sont rejetés dans le cas des paramètres de requête et convertis en chaînes de caractères quand ils apparaissent dans le résultat d'une requête. Pour contourner ce deuxième cas, la requête peut être quelque fois écrite de façon à ce que le type composite apparaisse comme une ligne de résultat plutôt que comme le champ d'une ligne du résultat. Autrement, la chaîne résultante peut être analysée manuellement mais cette approche n'est pas recommendée car une version future pourrait demander de refaire l'analyse de la chaîne en retour.
Quand vous préparez un plan en utilisant le module PL/Python, il est automatiquement sauvegardé. Lisez la documentation SPI (Chapitre 43, Interface de programmation serveur) pour une description complète. Pour en avoir une utilisation réelle via des appels de fonctions, vous avez besoin d'utiliser un dictionnaire de stockage persistent SD ou GD (voir Section 42.4, « Sharing Data »). Par exemple :
CREATE FUNCTION utilise_plan_sauvegarde() RETURNS trigger AS $$ if SD.has_key("plan"): plan = SD["plan"] else: plan = plpy.prepare("SELECT 1") SD["plan"] = plan # reste de la fonction $$ LANGUAGE plpythonu;
Les fonctions accédant à la base de données peuvent rencontrer des erreurs, qui forceront leur annulation et lèveront une exception. plpy.execute et plpy.prepare peuvent lancer une instance d'une sous-classe de plpy.SPIError, qui terminera par défaut la fonction. Cette erreur peut être gérée comme toutes les autres exceptions Python, en utilisant la construction try/except. Par exemple :
CREATE FUNCTION essaie_ajout_joe() RETURNS text AS $$ try: plpy.execute("INSERT INTO utilisateurs(nom) VALUES ('joe')") except plpy.SPIError: return "quelque chose de mal est arrivé" else: return "Joe ajouté" $$ LANGUAGE plpythonu;
La classe réelle de l'exception levée correspond à la condition spécifique qui a causé l'erreur. Référez-vous à Tableau A.1, « Codes d'erreur de PostgreSQL™ » pour une liste des conditions possibles. Le module plpy.spiexceptions définit une classe d'exception pour chaque condition PostgreSQL™, dérivant leur noms du nom de la condition. Par exemple, division_by_zero devient DivisionByZero, unique_violation devient UniqueViolation, fdw_error devient FdwError, et ainsi de suite. Chacune de ces classes d'exception hérite de SPIError. Cette séparation rend plus simple la gestion des erreurs spécifiques. Par exemple :
CREATE FUNCTION insere_fraction(numerateur int, denominateur int) RETURNS text AS $$ from plpy import spiexceptions try: plan = plpy.prepare("INSERT INTO fractions (frac) VALUES ($1 / $2)", ["int", "int"]) plpy.execute(plan, [numerateur, denominateur]) except spiexceptions.DivisionByZero: return "denominateur doit être différent de zéro" except spiexceptions.UniqueViolation: return "a déjà cette fraction" except plpy.SPIError, e: return "autre erreur, SQLSTATE %s" % e.sqlstate else: return "fraction insérée" $$ LANGUAGE plpythonu;
Notez que, comme toutes les exceptions du module plpy.spiexceptions héritent de SPIError, une clause except la gérant récupèrera toutes les erreurs d'accès aux bases.
Comme alternative à la gestion des différentes conditions d'erreur, vous pouvez récupérer l'exception SPIError et déterminer la condition d'erreur spécifique dans le bloc except en recherchant l'attribut sqlstate de l'objet exception. Cet attribut est une chaîne contenant le code d'erreur « SQLSTATE ». Cette approche fournit approximativement la même fonctionnalité.