De manière générale, le but de PL/Python est de fournir une relation « naturelle » entre PostgreSQL et le monde Python. Ces règles relationelles sont décrites ci-dessous.
Quand une procédure stockée PL/python est appelée, les paramètres de la fonction sont convertis de leur type de données PostgreSQL vers un type correspondant en Python :
Le type PostgreSQL boolean
est converti vers le type Python
bool
.
Les types PostgreSQL smallint
, int
,
bigint
et oid
sont convertis vers le type
Python int
.
Les types PostgreSQL real
et double
sont
convertis vers le type Python float
.
Le type PostgreSQL numeric
est converti vers le type
Python Decimal
. Ce type est importé à partir du paquet
cdecimal
s'il est disponible. Dans le cas contraire,
decimal.Decimal
est utilisé à partir de la bibliothèque
standard. cdecimal
est bien plus performant que
decimal
. Néanmoins, avec Python 3.3 et les versions ultérieures,
cdecimal
a été intégré dans la bibliothèque standard
sous le nom de decimal
, donc la différence n'est plus
valide.
Le type PostgreSQL bytea
est converti vers le type Python
bytes
.
Tous les autres types de données, incluant les types chaînes de
caractères de PostgreSQL, sont convertis vers un type Python
str
(en Unicode comme toutes les chaînes Python).
Pour les données non scalaires, voir ci-dessous.
Quand une fonction PL/python renvoie des données, la valeur de retour est convertie en type de données PostgreSQL comme suit:
Quand le type de la valeur PostgreSQL renvoyée est boolean
,
la valeur de retour sera évaluée en fonction des règles
Python. Ainsi, les 0 et les chaines vides sont
fausses, mais la valeur 'f'
est vraie.
Quand le type de retour PostgreSQL est bytea
, la valeur de
retour sera convertie vers le type Python bytes
en utilisant
les fonctions internes de Python, et le résultat sera converti de nouveau
en bytea
.
Pour tous les autres types de données renvoyées, la valeur de retour est
convertie en une chaîne de caractère en utilisant la fonction Python interne
str
, et le résultat est passé à la fonction d'entrée du
type de données PostgreSQL. (si la valeur Python est un flottant, il est
converti en utilisant la fonction interne repr
au lieu de
str
, pour éviter la perte de précision.)
Les chaînes sont automatiquement converties dans l'encodage du serveur PostgreSQL quand elles lui sont passées à PostgreSQL.
Pour les données non scalaires, voire ci dessous.
Notez que les erreurs logiques entre le type de retour déclaré dans PostgreSQL et le type de l'objet Python renvoyé ne sont pas détectées. La valeur sera convertie dans tous les cas.
Si une valeur SQL NULL est passée à une fonction,
la valeur de l'argument apparaîtra comme None
au niveau de
Python. Par exemple, la définition de la fonction pymax
indiquée dans Section 46.1 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 plpython3u;
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 valeurs de type tableaux SQL sont passées via PL/Python comme des listes Python. Pour renvoyer une valeur de type tableau SQL par une fonction PL/Python, renvoyez une liste Python :
CREATE FUNCTION return_arr() RETURNS int[] AS $$ return [1, 2, 3, 4, 5] $$ LANGUAGE plpython3u; SELECT return_arr(); return_arr ------------- {1,2,3,4,5} (1 row)
Les tableaux multi-dimensionnels sont passé dans PL/Python en tant que listes Python imbriquées. Un tableau à 2 dimensions est une liste de liste, par exemple. Quand une fonction PL/Python renvoie un tableau SQL multi-dimensionnel, les listes internes doivent avoir la même taille à chaque niveau. Par exemple :
CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$ plpy.info(x, type(x)) return x $$ LANGUAGE plpython3u; SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]); INFO: ([[1, 2, 3], [4, 5, 6]], <type 'list'>) test_type_conversion_array_int4 --------------------------------- {{1,2,3},{4,5,6}} (1 row)
Les autres séquences Python, comme les tuples, sont également acceptées pour compatibilité descendante avec les versions 9.6 et inférieures de PostgreSQL, quand les tableaux multi-dimensionnels n'étaient pas supportés. Cependant, ils sont toujours traités comme des tableaux à une dimension, car ils sont ambigus avec les types composites. Pour la même raison, quand un type composite est utilisé dans un tableau multi-dimensionnel, il doit être représenté par un tuple, plutôt que par une liste.
Notez que, avec Python, les chaînes sont des séquences, ce qui peut avoir des effets indésirables qui peuvent être familiers aux codeurs Python :
CREATE FUNCTION return_str_arr() RETURNS varchar[] AS $$ return "hello" $$ LANGUAGE plpython3u; SELECT return_str_arr(); return_str_arr ---------------- {h,e,l,l,o} (1 row)
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 plpython3u;
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 :
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 liste : return [ nom, valeur ] $$ LANGUAGE plpython3u;
Pour renvoyer NULL dans une colonne, insérez None
à la
position correspondante.
Quand un tableau de types composites est retourné, il ne peut pas être retourné comme une liste, car il est ambigu de savoir si la liste Python représente un type composite ou une autre dimension de tableau.
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 plpython3u;
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é.
__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 plpython3u;
Les fonctions ayant des paramètres OUT
sont
aussi supportées. Par exemple :
CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$ return (1, 2) $$ LANGUAGE plpython3u; SELECT * FROM multiout_simple();
Les paramètres en sortie de procédures sont renvoyés de la même façon. Par exemple :
CREATE PROCEDURE python_triple(INOUT a integer, INOUT b integer) AS $$ return (a * 3, b * 3) $$ LANGUAGE plpython3u; CALL python_triple(5, 10);
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 :
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 plpython3u;
__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 plpython3u;
yield
)
CREATE FUNCTION greet (how text) RETURNS SETOF greeting AS $$ for who in [ "World", "PostgreSQL", "PL/Python" ]: yield ( how, who ) $$ LANGUAGE plpython3u;
Les fonctions renvoyant des ensembles et ayant des paramètres
OUT
(en utilisant RETURNS SETOF
record
) sont aussi supportées. Par exemple :
CREATE FUNCTION multiout_simple_setof(n integer, OUT integer, OUT integer) RETURNS SETOF record AS $$ return [(1, 2)] * n $$ LANGUAGE plpython3u; SELECT * FROM multiout_simple_setof(3);