"La Gaceta de Linux...¡haciendo de Linux algo un poco más divertido!"
Pl/Python y Cursores en Pl/Pgsql para PostgreSQL
Traducción al español por Miguel Angel Rojas Aquino
el día 22 de Julio 2002, para La Gaceta de Linux
- Propósito
- Compilando PostgreSQL
- Ejecutando PL/Python y Pl/PgSQL con Cursores
- Sugerencias para el futuro
- Conclusiones
- 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:
- Edita src/pl/plperl/Makefile.PL y comenta las líneas de la 14 a la 34.
- Edita src/include/pg_config.h.in y cambia de
#define INDEX_MAX_KEYS 16
a
#define INDEX_MAX_KEYS 512
- 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
-
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
- initdb
- /usr/local/pg72/bin/pg_ctl -D /usr/local/pg72/data -l /tmp/pg_logfile start
- createlang plperl template1
- createlang plpython template1
- createlang plpgsql template1
- 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:
- 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.
- 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.
- 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
-
Configuración estándar de Base de datos con Perl y PostgreSQL: Parte 3
-
Parte 2: PostgreSQL: procedimientos en Perl con PL/pgSQL
-
Parte 1: PostgreSQL: procedimientos en Perl con PL/pgSQL.
- Un artículo anterior sobre
configuración de Servidores Web y de Bases de Datos.
-
Artículos de Branden Williams sobre PostgreSQL.
-
http://techdocs.postgresql.org/oresources.php
-
http://techdocs.postgresql.org/
- Algunos enlaces que no tienen nada que ver con este artículo, pero
que estoy considerando para futuros artículos.
-
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