El módulo de lenguaje PL/Python importa automáticamente un módulo de Python
llamado plpy. Las funciones y constantes en
este módulo están disponibles en el código de Python como
plpy..
foo
El módulo plpy proporciona varias funciones para ejecutar
comandos de base de datos:
plpy.execute(query [, limit])
Llamar a plpy.execute con una cadena de consulta y un
argumento de límite de filas opcional hace que se ejecute la consulta y se devuelva el resultado
en un objeto de resultado.
Si se especifica limit y es mayor que
cero, entonces plpy.execute recupera como máximo
limit filas, como si la consulta
incluyera una cláusula LIMIT. Omitir limit o
especificarlo como cero hace que no haya límite de filas.
El objeto de resultado emula un objeto de lista o diccionario. Se puede acceder al objeto de resultado por número de fila y nombre de columna. Por ejemplo:
rv = plpy.execute("SELECT * FROM my_table", 5)
devuelve hasta 5 filas de my_table. Si
my_table tiene una columna
my_column, se accedería a ella como:
foo = rv[i]["my_column"]
El número de filas devueltas se puede obtener utilizando la función incorporada
len.
El objeto de resultado tiene estos métodos adicionales:
nrows()
Devuelve el número de filas procesadas por el comando. Ten en cuenta que esto
no es necesariamente lo mismo que el número de filas devueltas. Por
ejemplo, un comando UPDATE establecerá este valor pero
no devolverá ninguna fila (a menos que se use RETURNING).
status()
El valor de retorno de SPI_execute().
colnames()coltypes()coltypmods()Devuelven una lista de nombres de columnas, una lista de OIDs de tipos de columnas y una lista de modificadores de tipo específicos para las columnas, respectivamente.
Estos métodos lanzan una excepción cuando se llaman en un objeto de resultado de
un comando que no produjo un conjunto de resultados, por ejemplo,
UPDATE sin RETURNING, o
DROP TABLE. Pero es correcto usar estos métodos en
un conjunto de resultados que contenga cero filas.
__str__()
El método estándar __str__ está definido para que
sea posible, por ejemplo, depurar los resultados de la ejecución de consultas
utilizando plpy.debug(rv).
El objeto de resultado se puede modificar.
Ten en cuenta que llamar a plpy.execute hará que todo el
conjunto de resultados se lea en memoria. Solo usa esa función cuando estés
seguro de que el conjunto de resultados será relativamente pequeño. Si no quieres
correr el riesgo de un uso excesivo de memoria al obtener resultados grandes,
usa plpy.cursor en lugar
de plpy.execute.
plpy.prepare(query [, argtypes])plpy.execute(plan [, arguments [, limit]])
plpy.prepare prepara el plan de ejecución para una
consulta. Se llama con una cadena de consulta y una lista de tipos de parámetros,
si tienes referencias a parámetros en la consulta. Por ejemplo:
plan = plpy.prepare("SELECT last_name FROM my_users WHERE first_name = $1", ["text"])
text es el tipo de la variable que pasarás
para $1. El segundo argumento es opcional si no
deseas pasar ningún parámetro a la consulta.
Después de preparar una sentencia, utilizas una variante de la
función plpy.execute para ejecutarla:
rv = plpy.execute(plan, ["name"], 5)
Pasa el plan como primer argumento (en lugar de la cadena de consulta), y una lista de valores para sustituir en la consulta como segundo argumento. El segundo argumento es opcional si la consulta no espera ningún parámetro. El tercer argumento es el límite de filas opcional como antes.
Alternativamente, puedes llamar al método execute en
el objeto de plan:
rv = plan.execute(["name"], 5)
Los parámetros de la consulta y los campos de las filas de resultados se convierten entre los tipos de datos de PostgreSQL y Python como se describe en Section 44.2.
Cuando preparas un plan utilizando el módulo PL/Python, se guarda automáticamente.
Lee la documentación de SPI (Chapter 45) para obtener una
descripción de lo que esto significa. Para hacer un uso efectivo de esto
a través de las llamadas a funciones, se debe usar uno de los diccionarios de almacenamiento persistente
SD o GD (consulta
Section 44.3). Por ejemplo:
CREATE FUNCTION usesavedplan() RETURNS trigger AS $$
if "plan" in SD:
plan = SD["plan"]
else:
plan = plpy.prepare("SELECT 1")
SD["plan"] = plan
# resto de la función
$$ LANGUAGE plpython3u;
plpy.cursor(query)plpy.cursor(plan [, arguments])
La función plpy.cursor acepta los mismos argumentos
que plpy.execute (excepto por el límite de filas) y devuelve
un objeto de cursor, que te permite procesar grandes conjuntos de resultados en fragmentos
más pequeños. Al igual que con plpy.execute, se puede utilizar una cadena de consulta
o un objeto de plan junto con una lista de argumentos, o
la función cursor se puede llamar como un método del
objeto de plan.
El objeto de cursor proporciona un método fetch que acepta
un parámetro entero y devuelve un objeto de resultado. Cada vez que
llames a fetch, el objeto devuelto contendrá el siguiente
lote de filas, nunca mayor que el valor del parámetro. Una vez que se agoten todas las filas,
fetch comenzará a devolver un objeto de resultado vacío.
Los objetos de cursor también proporcionan una
interfaz de
iterador, entregando una fila a la vez hasta que se agoten todas las filas.
Los datos obtenidos de esa manera no se devuelven como objetos de resultado, sino
más bien como diccionarios, correspondiendo cada diccionario a una sola fila de resultado.
Un ejemplo de dos formas de procesar datos de una tabla grande es:
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 plpython3u;
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 plpython3u;
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])) # o: = list(plan.cursor([2]))
return len(rows)
$$ LANGUAGE plpython3u;
Los cursores se eliminan automáticamente. Pero si deseas liberar explícitamente
todos los recursos retenidos por un cursor, utiliza el método close.
Una vez cerrado, ya no se pueden obtener más datos de un cursor.
No confundas los objetos creados por plpy.cursor con
los cursores de DB-API definidos por la
especificación del
API de base de datos de Python. No tienen nada en común
excepto el nombre.
Las funciones que acceden a la base de datos pueden encontrar errores, lo que
hará que se aborten y lancen una excepción. Tanto
plpy.execute como
plpy.prepare pueden lanzar una instancia de una subclase de
plpy.SPIError, que por defecto terminará
la función. Este error se puede manejar como cualquier otra
excepción de Python, utilizando la estructura try/except.
Por ejemplo:
CREATE FUNCTION try_adding_joe() RETURNS text AS $$
try:
plpy.execute("INSERT INTO users(username) VALUES ('joe')")
except plpy.SPIError:
return "something went wrong"
else:
return "Joe added"
$$ LANGUAGE plpython3u;
La clase real de la excepción lanzada corresponde a la
condición específica que causó el error. Consulta la
Table A.1 para obtener una lista de las condiciones posibles.
El módulo plpy.spiexceptions define una clase de excepción
para cada condición de PostgreSQL, derivando sus
nombres del nombre de la condición. Por ejemplo,
division_by_zero se convierte en DivisionByZero,
unique_violation se convierte en UniqueViolation,
fdw_error se convierte en FdwError, y así sucesivamente.
Cada una de estas clases de excepción hereda de SPIError. Esta
separación facilita el manejo de errores específicos, por ejemplo:
CREATE FUNCTION insert_fraction(numerator int, denominator int) RETURNS text AS $$
from plpy import spiexceptions
try:
plan = plpy.prepare("INSERT INTO fractions (frac) VALUES ($1 / $2)", ["int", "int"])
plpy.execute(plan, [numerator, denominator])
except spiexceptions.DivisionByZero:
return "denominator cannot equal zero"
except spiexceptions.UniqueViolation:
return "already have that fraction"
except plpy.SPIError as e:
return "other error, SQLSTATE %s" % e.sqlstate
else:
return "fraction inserted"
$$ LANGUAGE plpython3u;
Ten en cuenta que debido a que todas las excepciones del módulo
plpy.spiexceptions heredan de
SPIError, una cláusula except
que la maneje capturará cualquier error de acceso a la base de datos.
Como una forma alternativa de manejar diferentes condiciones de error, puedes
capturar la excepción SPIError y determinar la
condición de error específica dentro del bloque except
mirando el atributo sqlstate del objeto de excepción.
Este atributo es un valor de cadena que contiene el código de error
“SQLSTATE”. Este enfoque proporciona aproximadamente el mismo funcionamiento.