Bases de datos y persistencia

Uso de generadores python en Postgres

bases de datos y persistencia

Siguiendo con la colección de recetas que combinan postgres y python, en esta recetas veremos como utilizar los generadores de python dentro de postgres. Por ejemplo, como un secuenciador.

Uno de los problemas que nos encontraremos es que la comunicación entre python y postgres es que se limita casi exclusivamente a los tipos básicos. Si queremos almacenar un "generador" no nos quedará más remedio que almacenarlo en memoria de modo que sea localizado por las diferentes ejecuciones del secuenciador. Para este cometido, en PL/Python tenemos dos diccionarios: SD (static data) y GD (global data). El GD está disponible en cualquier procedimiento python, y no conviene utilizado si no es necesario. Utilizaremos el SD para conservar nuestro generador entre llamadas a nuestra función. Este diccionario sólo existe durante la sessión en curso, con lo que siempre habrá que comprobar que tenemos el generador en memoria para crearlo si no fuera así. Una mejora sería guardar el generador en la propia base de datos como una cadena de texto, pero tal vez para otra ocasión.:

-- Creación del secuenciador
CREATE FUNCTION mysec()
  RETURNS SETOF text AS '
if not SD.has_key("genfun"):
  def genfun():
    i=0L
    while True:
      yield i
      i=2*i+1 #crear una progresión extraña
  SD["genfun"]=genfun()

f=SD["genfun"]

return str(f.next())' LANGUAGE 'plpythonu';

La secuencia enseguida sobrepasa los valores máximos que puede almacenarse en un dato entero de postgres. Para poder trabajar con el tipo 'longint' de python se debe devolver el valor como TEXT. Podemos probar el funcionamiento de este generador:

SELECT mysec() LIMIT 100;

             mysec
--------------------------------
 0
 1
 3
 7
 15
 31
 63
 127
 255
 511
 1023
 2047
  ...........
  ...........
 4951760157141521099596496895
 9903520314283042199192993791
 19807040628566084398385987583
 39614081257132168796771975167
 79228162514264337593543950335
 158456325028528675187087900671
 316912650057057350374175801343
 633825300114114700748351602687
(100 filas)

Secuenciador python para postgres

bases de datos y persistencia

PostgreSQL posee la capacidad de definir distintos secuenciadores que sirven para dar valores por defecto a campos numéricos con lo que numerar los registros que vamos introduciendo. Con los lenguajes procedurales es posible llevar más allá estas posibilidades, y como muestra un botón: un secuenciador fibonacci hecho en python:

-- Creación de la tabla de prueba
CREATE TABLE prueba (id integer, nombre text);

-- Generador de números de fibonacci
CREATE FUNCTION fibgen() RETURNS integer AS '
#buscar los dos valores mayores
r=plpy.execute("select id from prueba order by id desc limit 2" )

if len(r)==2:
  a,b=r[1]["id"],r[0]["id"]
elif len(r)==1:
  a,b=r[0]["id"],0
else:
  a,b=1,0

return a+b'
LANGUAGE 'plpythonu';

-- Asignar el secuenciador
ALTER TABLE prueba
  ALTER COLUMN id SET DEFAULT fibgen();

Podemos probar su funcionamiento introduciendo algunos valores

INSERT INTO prueba(nombre) VALUES ('Primero');
INSERT INTO prueba(nombre) VALUES ('Segundo');
INSERT INTO prueba(nombre) VALUES ('Tercero');
INSERT INTO prueba(nombre) VALUES ('Cuarto');
SELECT * FROM prueba;

--  id | nombre
-- ----+---------
--   1 | Primero
--   1 | Segundo
--   2 | Tercero
--   3 | Cuarto
-- (4 filas)

Si forzamos el valor del campo, vemos que el secuenciador se "ajusta" a los nuevos parámetros

INSERT INTO prueba(id,nombre) VALUES(100,'Cien');
INSERT INTO prueba(nombre) VALUES('CienUno');
INSERT INTO prueba(nombre) VALUES('CienDos');
SELECT * FROM prueba;

--  id | nombre
-- ----+---------
--   1 | Primero
--   1 | Segundo
--   2 | Tercero
--   3 | Cuarto
-- 100 | Cien
-- 103 | CienUno
-- 203 | CienDos
-- (7 filas)

Manejo de BLOBs con PostgreSQL

bases de datos y persistencia

No existe mucha documentación sobre el manejo de BLOBs con postgres desde python. Aquí os dejo una receta para tal fin que espero os sea útil. Utilizo el interface PygreSQL.