El lenguaje procedural plpython permite escribir funciones python para la base de datos relacional PostgreSQL.
Como se puede acceder a todas las funciones de python, no debe usarse para usuarios no confiados, por eso se lo llama plpythonu (u=untrusted).
Para m谩s informaci贸n ver Sitio del Grupo de Usuarios de PostgreSQL Argentina, secci贸n PlPython
Funciones ("Procedimientos almacenados")
El cuerpo de una funcion plpythonu es simplemente un script de Python. Cuando la funci贸n es llamada, sus argumentos son pasados como elementos de una lista args
; los argumentos por nombre son pasados como variables ordinarias. El resultado es devuelto de la manera usual, con un return
o un yield
(en el caso que devuelvan un conjunto de resultados)
Los valores NULL
de PostgreSQL equivalen a None
en Python.
Est谩 disponible el diccionario SD
para almacenar datos entre cada llamada a funci贸n, y el diccionario globar GD
para usar desde todas las funciones.
Nota: PostgreSQL 8.1 no soporta argumentos por nombre, recibir valores compuestos, devolver listas/tuplas o usar generadores.
Ejemplo simple
Calcular el valor m谩ximo entre dos enteros, descartando valores nulos:
Recibir tipos compuestos
Las funciones plpython pueden recibir tipos compuestos (ej.registros de tablas) como diccionarios:
Devolver tipos compuestos
Los tipos compuestos pueden ser devueltos como secuencias (tuplas o listas), diccionarios u objetos. En este ejemplo se devuelve un tipo compuesto representando una persona:
CREATE TYPE persona AS ( nombre TEXT, apellido TEXT ); CREATE FUNCTION crear_persona (nombre TEXT, apellido TEXT) RETURNS persona AS $$ return [ nombre, apellido ] # o como tupla: return ( nombre, apellido ) # o como diccionario: return { "nombre": nombre, "apellido": apellido } $$ LANGUAGE plpythonu; CREATE FUNCTION crear_persona (nombre TEXT, persona TEXT) RETURNS persona AS $$ class Persona: def __init__ (self, n, a): self.nombre = n self.apellido = a return Persona(nombre, apellido) $$ LANGUAGE plpythonu;
Devolver m煤ltiples tipos escalares o compuestos (''set-of'')
Se puede devolver m煤ltiples valores (usando listas/tuplas, iteradores o generadores). En este ejemplo se devuelven varios saludos:
CREATE TYPE saludo AS ( mensaje TEXT, -- hola a_quien TEXT -- mundo ); CREATE FUNCTION saludar (mensaje TEXT) RETURNS SETOF saludo AS $$ # devolver una tupla conteniendo lista de tipos compuestos # todas las otras combinaciones son posibles return ( [ mensaje, "Mundo" ], [ mensaje, "PostgreSQL" ], [ mensaje, "PL/Python" ] ) $$ LANGUAGE plpythonu; CREATE FUNCTION saludar_generador (mensaje TEXT) RETURNS SETOF saludo AS $$ for a_quien in [ "Mundo", "PostgreSQL", "PL/Python" ]: yield ( mensaje, a_quien ) $$ LANGUAGE plpythonu;
Disparadores (Triggers)
Cuando una funci贸n plpython es usada en un disparador, el diccionario TD
contiene:
TD["new"]
: valores nuevos de la fila afectada (diccionario)TD["old"]
: valores viejos de la fila afectada (diccionario)TD["event"]
: tipo de evento "INSERT", "UPDATE", "DELETE", o "UNKNOWN"TD["when"]
: momento en que se ejecut贸: "BEFORE" (antes del commit), "AFTER" (despues del commit), o "UNKNOWN"TD["level"]
: nivel al que se ejecut贸: "ROW" (por fila), "STATEMENT" (por sentencia), o "UNKNOWN"TD["name"]
: nombre del disparadorTD["table_name"]
: nombre de la tabla en que se dispar贸TD["table_schema"]
: esquema en el que se dispar贸TD["relid"]
: OID de la tabla que dispar贸Si el comando
CREATE TRIGGER
incluy贸 argumentos, estos estar谩n disponibles en la listaTD["args"]
Si TD["when"]
es BEFORE, se puede devolver None
or "OK" para indicar que la fila no se modific贸, "SKIP" para abortar el evento, o "MODIFY" para indicar que hemos modificado la fila.
Acceso a la base de datos
Automaticamente se importa un m贸dulo llamado plpy
.
Generar mensajes y lanzar errores
Este m贸dulo incluye funciones de plpy.debug(msg)
, plpy.log(msg)
, plpy.info(msg)
, plpy.notice(msg)
, plpy.warning(msg)
, plpy.error(msg)
, y plpy.fatal(msg)
plpy.error
y plpy.fatal
en realidad disparan una excepci贸n python, si no se controla, se propaga y causa que la transacci贸n se aborte. Equivalente a llamar raise plpy.ERROR(msg)
y raise plpy.FATAL(msg)
, respectivamente
Las otras funciones solo generan mensajes en los distintos niveles de prioridad.
Preparar y ejecutar consultas
Adicionalmente, el m贸dulo plpy
provee dos funciones: execute
y prepare
.
Llamar a plpy.execute(query, limit)
con una consulta (query: string) y un l铆mite de registros opcional (limit), permite ejecutar la consulta y devuelve los resultados en un objeto que emula una lista de diccionarios, pudiendo acceder por n煤mero de fila y nombre de columna. Tiene tres m茅todos adicionales: nrows
que devuelve el n煤mero de filas, y status
.
Ejemplo:
La funci贸n plpy.prepare(query,[parameter_types])
, prepara el plan de ejecuci贸n para una consulta, se le pasa la consulta como string y la lista de tipos de par谩metros:
plan = plpy.prepare("SELECT apellido FROM usuario WHERE nombre = $1 AND casado = $2 ", [ "text", "boolean" ])
text
y boolean
son los tipos de la variables que se pasara como par谩metros ($1 y $2).
Despues de preparar la sentencia, usar la funci贸n plpy.execute
para ejecutarla:
Se pasa el plan como primer argumento, los par谩metros como segundo (en este caso, busca nombre="Mariano" y si esta casado). El l铆mite (tercer argumento) es opcional.
Al preparar un plan, este se almacena para usarlo posteriormente. Para usarlo eficazmente entre llamada y llamada, se debe usar un diccionario de almacenamiento persistente (SD
o GD
) para guardarlo:
CREATE FUNCTION usar_plan_guardado() RETURNS trigger AS $$ if SD.has_key("plan"): plan = SD["plan"] # est谩 el plan, lo reutilizo else: # no esta el plan, lo creo y almaceno en el diccionario persistente plan = plpy.prepare("SELECT 1") SD["plan"] = plan # continua la funci贸n... $$ LANGUAGE plpythonu;