En general, el objetivo de PL/Python es proporcionar un mapeo “natural” entre el mundo de PostgreSQL y el de Python. Esto informa las reglas de mapeo de datos que se describen a continuación.
Cuando se llama a una función PL/Python, sus argumentos se convierten de su tipo de datos de PostgreSQL al tipo de Python correspondiente:
El tipo boolean de PostgreSQL se convierte al tipo bool de Python.
Los tipos smallint, int, bigint
y oid de PostgreSQL se convierten al tipo int de Python.
Los tipos real y double de PostgreSQL se convierten al
tipo float de Python.
El tipo numeric de PostgreSQL se convierte al
tipo Decimal de Python. Este tipo se importa del
paquete cdecimal si está disponible.
De lo contrario,
se usará decimal.Decimal de la biblioteca estándar.
cdecimal es significativamente más rápido
que decimal. Sin embargo, en Python 3.3 y versiones posteriores,
cdecimal se ha integrado en la
biblioteca estándar bajo el nombre decimal, por lo que ya no
hay ninguna diferencia.
El tipo bytea de PostgreSQL se convierte al tipo bytes de Python.
Todos los demás tipos de datos, incluidos los tipos de cadenas de caracteres de PostgreSQL,
se convierten a un tipo str de Python (en Unicode como todas las cadenas
de Python).
Para los tipos de datos no escalares, consulta más abajo.
Cuando una función PL/Python retorna, su valor de retorno se convierte al tipo de datos de retorno de PostgreSQL declarado de la siguiente manera:
Cuando el tipo de retorno de PostgreSQL es boolean, el
valor de retorno se evaluará para determinar su veracidad de acuerdo con las
reglas de Python. Es decir, 0 y la cadena vacía
son falsos, pero cabe destacar que 'f' es verdadero.
Cuando el tipo de retorno de PostgreSQL es bytea, el valor de retorno
se convertirá al tipo bytes de Python utilizando las funciones
incorporadas de Python correspondientes, y el resultado se convertirá a
bytea.
Para todos los demás tipos de retorno de PostgreSQL, el valor de retorno se convierte
a una cadena utilizando la función incorporada str de Python, y el
resultado se pasa a la función de entrada del tipo de datos de PostgreSQL.
(Si el valor de Python es un tipo float, se convierte utilizando
la función incorporada repr en lugar de str, para
evitar la pérdida de precisión).
Las cadenas se convierten automáticamente a la codificación del servidor PostgreSQL cuando se pasan a PostgreSQL.
Para los tipos de datos no escalares, consulta más abajo.
Ten en cuenta que las discrepancias lógicas entre el tipo de retorno declarado en PostgreSQL y el tipo de datos de Python del objeto de retorno real no se marcan como error; el valor se convertirá en cualquier caso.
Si se pasa un valor SQL null a una
función, el valor del argumento aparecerá como None en
Python. Por ejemplo, la definición de la función pymax
mostrada en Section 44.1 devolverá una respuesta incorrecta para entradas
null. Podríamos añadir STRICT a la definición de la función
para que PostgreSQL haga algo más razonable:
si se pasa un valor null, la función no se llamará en absoluto,
sino que simplemente devolverá un resultado null automáticamente. Alternativamente,
podríamos comprobar si hay entradas null en el cuerpo de la función:
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;
Como se muestra arriba, para devolver un valor SQL null desde una función
PL/Python, devuelve el valor None. Esto se puede hacer tanto si la
función es estricta (strict) como si no.
Los valores de array de SQL se pasan a PL/Python como una lista de Python. Para devolver un valor de array de SQL desde una función PL/Python, devuelve una lista de 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)
Los arrays multidimensionales se pasan a PL/Python como listas anidadas de Python. Un array bidimensional es una lista de listas, por ejemplo. Al devolver un array SQL multidimensional desde una función PL/Python, las listas internas en cada nivel deben ser todas del mismo tamaño. Por ejemplo:
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)
También se aceptan otras secuencias de Python, como las tuplas, para mantener la compatibilidad con las versiones 9.6 y anteriores de PostgreSQL, cuando no se admitían arrays multidimensionales. Sin embargo, siempre se tratan como arrays unidimensionales, porque son ambiguas con los tipos compuestos. Por la misma razón, cuando se utiliza un tipo compuesto en un array multidimensional, debe representarse mediante una tupla, en lugar de una lista.
Ten en cuenta que en Python, las cadenas son secuencias, lo que puede tener efectos no deseados que podrían resultar familiares a los programadores de 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)
Los argumentos de tipo compuesto se pasan a la función como mapeos (mappings) de Python. Los
nombres de los elementos del mapeo son los nombres de los atributos del tipo compuesto.
Si un atributo en la fila pasada tiene el valor null, tiene el valor
None en el mapeo. Aquí tienes un ejemplo:
CREATE TABLE employee (
name text,
salary integer,
age integer
);
CREATE FUNCTION overpaid (e employee)
RETURNS boolean
AS $$
if e["salary"] > 200000:
return True
if (e["age"] < 30) and (e["salary"] > 100000):
return True
return False
$$ LANGUAGE plpython3u;
Hay múltiples formas de devolver filas o tipos compuestos desde una función de Python. Los siguientes ejemplos asumen que tenemos:
CREATE TYPE named_value AS ( name text, value integer );
Un resultado compuesto se puede devolver como:
Los objetos de secuencia devueltos deben tener el mismo número de elementos que campos tiene el tipo de resultado compuesto. El elemento con índice 0 se asigna al primer campo del tipo compuesto, el 1 al segundo, y así sucesivamente. Por ejemplo:
CREATE FUNCTION make_pair (name text, value integer) RETURNS named_value AS $$ return ( name, value ) # o alternativamente, como lista: return [ name, value ] $$ LANGUAGE plpython3u;
Para devolver un valor SQL null para cualquier columna, inserta None en
la posición correspondiente.
Cuando se devuelve un array de tipos compuestos, no se puede devolver como una lista, porque es ambiguo si la lista de Python representa un tipo compuesto o otra dimensión de array.
El valor de cada columna del tipo de resultado se recupera del mapeo utilizando el nombre de la columna como clave. Ejemplo:
CREATE FUNCTION make_pair (name text, value integer)
RETURNS named_value
AS $$
return { "name": name, "value": value }
$$ LANGUAGE plpython3u;
Cualquier par clave/valor adicional en el diccionario es ignorado. Las claves faltantes se
tratan como errores.
Para devolver un valor SQL null para cualquier columna, inserta
None con el nombre de la columna correspondiente como clave.
__getattr__)Esto funciona de la misma manera que un mapeo. Ejemplo:
CREATE FUNCTION make_pair (name text, value integer)
RETURNS named_value
AS $$
class named_value:
def __init__ (self, n, v):
self.name = n
self.value = v
return named_value(name, value)
# o simplemente
class nv: pass
nv.name = name
nv.value = value
return nv
$$ LANGUAGE plpython3u;
También se admiten funciones con parámetros OUT. Por ejemplo:
CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$ return (1, 2) $$ LANGUAGE plpython3u; SELECT * FROM multiout_simple();
Los parámetros de salida de los procedimientos se devuelven de la misma manera. Por ejemplo:
CREATE PROCEDURE python_triple(INOUT a integer, INOUT b integer) AS $$ return (a * 3, b * 3) $$ LANGUAGE plpython3u; CALL python_triple(5, 10);
Una función PL/Python también puede devolver conjuntos de tipos escalares o compuestos. Hay varias formas de lograr esto porque el objeto devuelto se convierte internamente en un iterador. Los siguientes ejemplos asumen que tenemos el tipo compuesto:
CREATE TYPE greeting AS ( how text, who text );
Un resultado de conjunto se puede devolver desde un:
CREATE FUNCTION greet (how text) RETURNS SETOF greeting AS $$ # devuelve una tupla que contiene listas como tipos compuestos # todas las demás combinaciones también funcionan return ( [ how, "World" ], [ how, "PostgreSQL" ], [ how, "PL/Python" ] ) $$ LANGUAGE plpython3u;
__iter__ y
__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;
También se admiten funciones que devuelven conjuntos con parámetros OUT
(usando RETURNS SETOF record). Por ejemplo:
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);