"La Gaceta de Linux...¡haciendo de Linux algo un poco más divertido!"


Pl/Python y Cursores en Pl/Pgsql para PostgreSQL

Por Mark Nielsen

Traducción al español por Miguel Angel Rojas Aquino
el día 22 de Julio 2002, para La Gaceta de Linux


  1. Propósito
  2. Compilando PostgreSQL
  3. Ejecutando PL/Python y Pl/PgSQL con Cursores
  4. Sugerencias para el futuro
  5. Conclusiones
  6. Referencias

Propósito

El propósito de este artículo es el familiarizar a las personas con algunas de las nuevas técnicas de programación con lenguajes de procedimientos en PostgreSQL 7.2. Yo soy un gran fanático de los lenguajes orientados a procedimientos, y en general, PostgreSQL se lleva las palmas en lo que se refiere a procedimientos almacenados. Puedes crear muchos tipos diferentes de funciones tales como: SQL, Pl/PgSQL, Pl/Perl, y Pl/Tcl. Recientemente en 7.2, tambiéen contamos con Pl/Perlu y Pl/Python --- Perl no restringido y Python. La habilidad de emplear otro lenguaje de programación dentro de un servidor de base de datos hace la vida más sencilla para los programadores (incluso si hay alguna ineficiencia con Perl y Python). Durante algunos años, me molestaba el hecho de que los lenguajes de procedimientos no pudieran regresar mas que un solo elemento. Esto ha sido más o menos solucionado, pero no a la perfección. Una de las últimas áreas para hacer que mi vida sea diez veces más sencilla es el hacer que los lenguajes de procedimientos devuelvan más de un valor. Veremos una forma de lograr esto, pero tengo algunas sugerencias para mejorarlo.

NOTA: Estoy utilizando Red Hat 7.2 como base para este artículo. Algunas cosas pueden ser diferentes con tu versión de Linux.

Compilando PostgreSQL

Hay tres cosas que necesitamos saber al compilar PostgreSQL:
  1. Edita src/pl/plperl/Makefile.PL y comenta las líneas de la 14 a la 34.
  2. Edita src/include/pg_config.h.in y cambia de
    #define INDEX_MAX_KEYS 16
    a
    #define INDEX_MAX_KEYS 512
  3. Utiliza estos comandos para compilar PostgreSQL:
    adduser postgres
    mkdir /usr/local/pg72
    ./configure --prefix=/usr/local/pg72 --with-libraries=/usr/lib/perl5/5.6.0/i386-linux/CORE/libperl.a --with-perl --with-python
    make
    make install
    chown -R postgres /usr/local/pg72
    mkdir /home/postgres
    chown postgres /home/postgres
    su -l postgres
Puedes agregar odbc, tcl, y otros lenguajes, pero yo solamente estoy empleando Perl y Python como ejemplos por el momento. Después de ejecutar "make" y "make install", ejecuta estos comandos como el usuario postgresql

  1. PATH=/usr/local/pg72/bin:$PATH
    export PATH
    LD_LIBRARY_PATH=/usr/local/pg72/lib
    export LD_LIBRARY_PATH
    export PGDATA=/usr/local/pg72/data
    export PGLIB=/usr/local/pg72/lib
  2. initdb
  3. /usr/local/pg72/bin/pg_ctl -D /usr/local/pg72/data -l /tmp/pg_logfile start
  4. createlang plperl template1
  5. createlang plpython template1
  6. createlang plpgsql template1
  7. createdb postgres

Ejecutando Pl/Python y Pl/PgSQL con Cursores

He aquí una lista de comandos que puedes ejecutar utilizando el comando "psql template1". Esto asume que se ha creado una base de datos "postgres".

Ejecuta estos comandos:


\c postgres

drop table contact;
create table contact (
first text, last text, phone text, address text,
city text,state text, zip text
);

drop function replace_e_to_a(text);
CREATE FUNCTION replace_e_to_a(text) RETURNS text AS
'
import re
Text1 = re.sub(''e'', ''a'',args[0])
return Text1
'
LANGUAGE 'plpython';

drop function replace_numbers_to_z(text);
CREATE FUNCTION replace_numbers_to_z(text) RETURNS text AS
'
import re
Text1 = re.sub(''[0-9]'', ''z'',args[0])
return Text1
'
LANGUAGE 'plpython';

insert into contact values ('Mark','Nielsen','123-123-1234',
  '1234 Somewhere St.', 'Some City 123', 'TX','12345-1234');
insert into contact values ('Mark','Nielsen2','123-123-1234',
  '3456 Somewhere St.', 'Some City 444', 'MI','12345-1234');
insert into contact values ('Mark','Nielsen3','123-123-1234',
  '5678 Somewhere St.', 'Some City 654', 'AX','12345-1234');

select first, last, address  from contact;

drop function select_contact();
CREATE FUNCTION select_contact () RETURNS refcursor AS '
DECLARE
    cursor1 CURSOR FOR select replace_e_to_a(first)as first, 
                        replace_e_to_a(last) as last,
                        replace_numbers_to_z(address) as address
            from contact;
BEGIN
 open cursor1;
 return (cursor1);
END;
' LANGUAGE 'plpgsql';

begin;
select select_contact();
FETCH cursor1; FETCH cursor1;FETCH cursor1;
end;
y la salida debe ser parecida a esto:
DROP
CREATE
DROP
CREATE
DROP
CREATE
INSERT 255188 1
INSERT 255189 1
INSERT 255190 1
 first |   last   |      address       
-------+----------+--------------------
 Mark  | Nielsen  | 1234 Somewhere St.
 Mark  | Nielsen2 | 3456 Somewhere St.
 Mark  | Nielsen3 | 5678 Somewhere St.
(3 rows)

DROP
CREATE
BEGIN
 select_contact 
----------------
 cursor1
(1 row)

 first |  last   |      address       
-------+---------+--------------------
 Mark  | Nialsan | zzzz Somewhere St.
(1 row)

 first |   last   |      address       
-------+----------+--------------------
 Mark  | Nialsan2 | zzzz Somewhere St.
(1 row)

 first |   last   |      address       
-------+----------+--------------------
 Mark  | Nialsan3 | zzzz Somewhere St.
(1 row)

COMMIT

Por medio de este ejemplo, puedes ver la forma en que Pl/pgSQL ejecuta los procedimientos Python (los procedimientos Pl/Python). No necesitas que Pl/pgSQL ejecute los procedimientos Pl/Python, yo lo hice así, pero no es indispensable. Puedes hacer uso de Perl o Python solamente para manipular los datos, pero no para modificar los datos en las tablas. Perl y Python solamente aceptan y devuelven datos, no hacen nada con la base de datos directamente. Asimismo, pl/perlu se instala cuando compilas PostgreSQL con soporte para Perl, lo cual es un buen detalle.

Sugerencias para el Futuro

Aún tengo tres grandes quejas/sugerencias:
  1. Al parecer los procedimientos sólo son capaces de devolver solo un "elemento". No pueden devolver dos. A mi me gustaría poder devolver al menos dos elementos --- un cursor y una bandera indicando si el cursor es válido o no. Los cursores NO hacen que los procedimientos devuelvan más de un valor, solamente devuelven el cursor, el cual podemos considerar como una referencia a varios elementos, no los elementos mismos. La manera en que compilo mis procedimientos es que estos tomen hasta 512 variables de entrada, pero solamente pueden devolver un "elemento". No entiendo muy bien por qué ocurre esto. He tratado de leer las discusiones en las listas de correo y los documentos, pero me pierdo en ello.
  2. No parece haber muchas cosas se puedan hacer a los cursores, tales como ver cuantas entradas contienen, si quedan entradas, si hay o no entradas, si se ejecutó correctamente, etc. Los cursores necesitan avanzar más. Quizá simplemente no tengo suficiente experiencia con los cursores como para hacer comentarios sobre ellos.
  3. Los procedimientos no pueden devolver renglones de datos. Además del hecho de que solamente pueden devolver un elemento, estos no pueden devolver renglones de un solo elemento o renglones que contengan múltiples elementos. El que pudieran devolver múltiples renglones con múltiples campos sería estupendo. Incluso si solamente quisiéramos devolver un renglón de datos, seria bueno poder devolver el cursor como un elemento, y su estado (correcto, incorrecto, cuantos, etc.) además. Si fuera posible devolver más de un elemento, también podríamos hacerlo de manera limitada o ilimitada por medio de un número al momento de compilación (tal como lo es en el caso de las entradas).
Me gusta el progreso de los lenguajes de procedimientos en PostgreSQL. Parecen estar bastante adelantados en relación a la mayoría de los servidores de bases de datos libres e incluso de varios de los servidores comerciales. Efectuar el siguiente paso lo haría realmente un servidor de base de datos que patea traseros. Yo trato de ayudar escribiendo artículos (N. del T.: yo traduciéndolos ;), ¿Tal vez puedas ayudar agregando otras características?

Esto no es realmente una queja sobre cursores, sino sobre DBD:Pg para Perl, y posiblemente sobre otras interfaces para PostgreSQL: los cursores no están realmente implementados, por lo que he podido observar. Por consiguiente, si Pl/PgSQL pudiera devolver múltiples renglones con múltiples elementos, esto resolvería este problema.

La única forma alterna que conozco de almacenar datos de un procedimiento es por medio de una tabla temporal que pueda ser accesible una vez que el procedimiento haya finalizado. Para mi, esto es algo extremo para el 99% de los datos que deseo obtener. Es extremo porque solamente deseo un renglón de datos y el crear una sola tabla para solamente un renglón de datos no vale la pena el esfuerzo.

Conclusiones.

Pl/Python finalmente me permitirá deshacerme de Perl de una vez por todas (yo solo me convertí a Python). Pl/PgSQL está cada vez más cerca de ser algo que me facilite el crear programas y procedimientos complicados -- solamente desearía que pudiera devolver múltiples elementos y no solamente referencias a un cursor o a otro elemento sencillo.

Lo triste de esto es, mi versión de DBD:Pg para Perl y mi interfaz Python no implementan cursores, y por lo tanto, estos no son útiles para mi en la mayoría de las veces, pero al menos se está mejorando. He encontrado algunas cosas buenas en http://developer.postgresql.org/todo.php que se ven prometedoras. Ya que los cursores no están realmente implementados en los lenguajes de programación que yo utilizo, si me veo en la necesidad de almacenar una gran cantidad de datos, probablemente necesite utilizar tablas temporales. Aún no comprendo el por qué un lenguaje de procedimientos no puede devolver datos tal como lo hace un comando sql normal. Cualquiera que sea la limitante, sería bueno que fuera superada.

Referencias

  1. Configuración estándar de Base de datos con Perl y PostgreSQL: Parte 3
  2. Parte 2: PostgreSQL: procedimientos en Perl con PL/pgSQL
  3. Parte 1: PostgreSQL: procedimientos en Perl con PL/pgSQL.
  4. Un artículo anterior sobre configuración de Servidores Web y de Bases de Datos.
  5. Artículos de Branden Williams sobre PostgreSQL.
  6. http://techdocs.postgresql.org/oresources.php
  7. http://techdocs.postgresql.org/
  8. Algunos enlaces que no tienen nada que ver con este artículo, pero que estoy considerando para futuros artículos.
  9. Si hay modificaciones a este artículo estarán disponibles en http://www.gnujobs.com/Articles/26/nielsen.html

Mark Nielsen

Mark trabaja en AudioBoomerang.com la cual crea, entrega y hace seguimientos de campañas de correo electrónico multimedia personalizado, web, y boletines. El trabaja como un consultor entregando productos finales a clientes de AudioBoomerang.com, tales como avanzados reportes estadísticos personalizados utilizados para perfiles demográficos o psicológicos para campañas futuras. En su tiempo libre, escribe artículos relacionados con el software libre (GPL) o Literatura Libre (FDL) y se encuentra involucrado con el centro de aprendizaje sin fines de lucro eastmont.net.


Copyright © 2002, Mark Nielsen.
Licencia de copia http://www.linuxgazette.com/copying.html
Publicado en el número 80 de Linux Gazette, Julio 2002