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
fournit plusieurs fonctions pour exécuter
des commandes sur la base de données :
plpy.execute
(query
[, max-rows
])
L'appel à plpy.execute
avec une chaîne pour la requête
et une limite de ligne optionnelle permet d'exécuter la requête et de
retourner le résultat dans un objet résultant.
L'objet résultant émule une liste ou un objet dictionnaire. L'objet résultant peut être accédé par le numéro de ligne et le nom de colonne. Par exemple :
rv = plpy.execute("SELECT * FROM my_table", 5)
retourne jusqu'à 5 lignes de my_table
.
Si my_table
possède une colonne my_column
,
elle pourra être accédée ainsi :
foo = rv[i]["my_column"]
Le nombre de lignes retournées peut être obtenu en utilisant la
fonction intégrée len
.
L'objet résultant contient ces méthodes additionnelles :
nrows
()
Retourne le nombre de lignes traitées par cette commande. Notez que
cela n'est pas nécessairement identique au nombre de lignes retournées.
Par exemple, une commande UPDATE
fixera cette valeur
mais ne retournera aucune ligne (sauf si RETURNING
est utilisé).
status
()
La valeur retournée par SPI_execute()
.
colnames
()
coltypes
()
coltypmods
()
Retourne respectivement une liste de noms de colonne, une liste de type OID de colonne et une liste de type de modifieurs spécifiques à un type pour les colonnes.
Ces méthodes lèvent une exception quand elles sont appelées sur un
objet résultant d'une commande n'ayant pas produit d'ensemble de
résultat, par ex,
UPDATE
sans RETURNING
, ou
DROP TABLE
. Il est cependant normal d'utiliser
ces méthodes sur un ensemble de résultat ne contenant aucune ligne.
__str__
()
La méthode standard __str__
est définie pour qu'il
soit possible de débugger les résultats de l'exécution d'une requête
en utilisant plpy.debug(rv)
.
L'objet résultant peut être modifié.
Notez que l'appel à plpy.execute
provoquera la lecture
de tout l'ensemble de résultat en mémoire. N'utilisez cette fonction que
lorsque vous êtes surs que l'ensemble de résultat sera relativement petit.
Si vous ne voulez pas risquer une utilisation excessive de mémoire pour
récupérer de gros ensembles, préférez plpy.cursor
à
plpy.execute
.
plpy.prepare
(query
[, argtypes
])
plpy.execute
(plan
[, arguments
[, max-rows
]])
plpy.prepare
prépare le plan d'exécution pour une requête.
Il faut l'appeler avec une chaîne contenant la requête et une liste de types
de paramètres, si vous avez des références à des paramètres dans cette requête.
Par exemple :
plan = plpy.prepare("SELECT last_name FROM my_users WHERE first_name = $1", ["text"])
text
est le type de variable qui sera passé à
$1
. Le second paramètre est optionel si vous
ne voulez pas fournir de paramètre à la requête.
Après avoir préparé une requête, il faut utiliser une variante
de la fonction plpy.execute
pour l'exécuter :
rv = plpy.execute(plan, ["name"], 5)
Il faut fournir le plan comme premier argument (à la place de la chaîne), et une liste de valeurs à substituer dans la requête comme second argument. Le deuxième argument est optionnel si la requête n'attend pas de paramètre. Le troisième argument est la limite de ligne optionnelle comme auparavant.
De manière alternative, vous pouvez appeler la méthode
execute
sur l'objet plan :
rv = plan.execute(["name"], 5)
Les paramètres de requête ainsi que les champs des lignes de résultat sont converties entre les types de données de PostgreSQL et de Python comme décrit dans Section 45.3.
Quand un plan est préparé en utilisant le module PL/Python, il est
automatiquement sauvegardé. Voir la documentation de SPI (Chapitre 46)
pour une description de ce que cela signifie. Afin d'utiliser efficacement
ces appels de fonction, il faut utiliser un des dictionnaires de stockage
persistant SD
ou GD
(voir
Section 45.4). Par exemple :
CREATE FUNCTION usesavedplan() RETURNS trigger AS $$ if "plan" in SD: plan = SD["plan"] else: plan = plpy.prepare("SELECT 1") SD["plan"] = plan # reste de la fonction $$ LANGUAGE plpythonu;
plpy.cursor
(query
)
plpy.cursor
(plan
[, arguments
])
La fonction plpy.cursor
accepte les mêmes arguments
que plpy.execute
(à l'exception de la limite de lignes)
et retourne un objet curseur, qui permet de traiter de gros ensembles de
résultats en plus petits morceaux. Comme avec plpy.execute
,
une chaîne de caractère ou un objet plan accompagné d'une liste d'arguments
peuvent être utilisés, ou la fonction cursor
peut
être appelée comme une méthode de l'objet plan.
L'objet curseur fournit une méthode fetch
qui requiert
en entrée un paramètre entier et retourne un objet résultat. À chaque appel
de fetch
, l'objet retourné contiendra la prochaine
série de lignes, mais jamais plus que la valeur passée en paramètre.
Une fois que toutes les lignes ont été épuisées, fetch
se met à retourner des objets résultat vides. Les objets curseurs
fournissent également une
interface d'itérateur, fournissant les lignes une par une jusqu'à épuisement.
Les données récupérées de cette façon ne sont pas retournées dans des objets
résultat, mais plutôt dans des dictionnaires, chacun correspondant à une unique
ligne de résultat.
Un exemple montrant deux façons de traiter des données dans une large table est:
CREATE FUNCTION count_odd_iterator() RETURNS integer AS $$ odd = 0 for row in plpy.cursor("select num from largetable"): if row['num'] % 2: odd += 1 return odd $$ LANGUAGE plpythonu; CREATE FUNCTION count_odd_fetch(batch_size integer) RETURNS integer AS $$ odd = 0 cursor = plpy.cursor("select num from largetable") while True: rows = cursor.fetch(batch_size) if not rows: break for row in rows: if row['num'] % 2: odd += 1 return odd $$ LANGUAGE plpythonu; CREATE FUNCTION count_odd_prepared() RETURNS integer AS $$ odd = 0 plan = plpy.prepare("select num from largetable where num % $1 <> 0", ["integer"]) rows = list(plpy.cursor(plan, [2])) # or: = list(plan.cursor([2])) return len(rows) $$ LANGUAGE plpythonu;
Les curseurs sont automatiquement libérés. Mais si vous voulez
libérer explicitement toutes les ressources retenues par un curseur,
il faut utiliser la méthode close
.
Une fois fermé, un curseur ne peut plus être utilisé pour retourner
des lignes.
Il ne faut pas confondre les objets créés par plpy.cursor
avec les curseurs DB-API comme définis par
la spécification Python Database API. Ils n'ont rien en commun si
ce n'est le nom.
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 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é.