Programaci´n de funciones en PL/pgSQL para o PostgreSQL

Roberto Andrade Fonseca. ABL Consultores, S.A. de C.V. 8 de febrero de 2002

1.

Objetivos
Presentar a los asistentes las ventajas de contar con funciones que manejen las ’reglas del negocio’ dentro de una base de datos, PostgreSQL en nuestro caso, para independizarlas del c´digo en que se programen los clientes. o Describir el lenguaje y mostrar ejemplos reales de funciones desarrolladas con PL/pgSQL.

2.

Introducci´n o

Es com´n que los desarrolladores de aplicaciones subutilicen las prestaciones u de las bases de datos relacionales modernas, en ocasiones aimplemente por deconocer las ventajas que le ofrecen o por desconocer su manejo. Dentro de PostgreSQL, la base de datos de c´digo abierto m´s poderosa, o a se pueden desarrollar funciones en varios lenguajes. El lenguaje PL/pgSQL es uno de los m´s utilizados dentro de PostgreSQL, debido a que guarda cierta a similitud con PL/SQL de Oracle y a su facilidad de uso. En este tutorial se mostrar´ la sintaxis, el control de flujo y otras caraca ter´ ısticas del lenguaje, adem´s de presentar´n algunos ejmeplos reales. a a

2.1.

Ventajas de usar PL/pgSQL

SQL es el lenguaje est´ndar para realizar consultas a un servidor de base de a datos. Cada sentencia SQL se ejecuta de manera individual por el servidor, lo cual implica que las aplicaciones cliente deben enviar cada consulta al servidor, esperar a que la procese, recibir los resultados, procesar los datos y despu´s e enviar la siguiente sentencia. Al usar PL/pgSQL es posible relizar c´lculos, manejo de cadenas y consultas a dentro del servidor de la base de datos, combinando el poder de un lenguaje procedimental y la facilidad de uso de SQL, minimizando el tiempo de conexi´n o entre el cliente y el servidor.

1

lada varchar. vamos a utilizar una base de datos que tiene el siguiente esquema: -. codigo_postal varchar. compania varchar.3 NUESTRA BASE DE DATOS DEL TUTORIAL 2 3. ap_paterno varchar NOT NULL. id_sistema_operativo int REFERENCES sistema_operativo. id_titulo int REFERENCES titulo. id_estado varchar REFERENCES estado. telefono1 varchar(8). fax varchar(8). id_pais char(2) NOT NULL REFERENCES pais. nombre varchar NOT NULL.Esquema de la base de datos del tutorial -. id_categoria int NOT NULL REFERENCES categoria DEFAULT 1. id_giro_empresa int REFERENCES giro_empresa. sexo char(1) CHECK (sexo in (’M’. ap_materno varchar. Todas las palabras clave y los identificadores pueden escribirse mezclando letras may´sculas y min´sculas.de PL/pgSQL CREATE TABLE asistente ( id_asistente SERIAL. PRIMARY KEY (id_asistente) ). colonia varchar(40). 4. Estructura de PL/pgSQL El lenguaje PL/pgSQL es estructura en bloques. url varchar. ciudad varchar. direccion varchar. ’F’)). u u Un bloque se definde de la siguiente manera: [<<label>>] [DECLARE declaraciones] . id_lugar_compra int REFERENCES lugar_compra. email varchar. telefono2 varchar(8). id_puesto int REFERENCES puesto. Nuestra base de datos del tutorial Para ejemplificar el uso de las funciones en PL/pgSQL.

5 COMENTARIOS. BEGIN RAISE NOTICE ’’Cantidad contiene aqu´ %’’. 5. ı -.Cantidad contiene aqu´ 80 ı END. --. No se debe confundir el uso de las sentencias de agrupamiento BEGIN/END de PL/pgSQL con los comandos de la base de datos que sirven para el control de las transacciones.Cantidad contiene aqu´ 50 ı RETURN cantidad. constantes y variables Comentarios Existen dos tipo de comentarios en PL/pgSQL. Pueden existir varios bloques o sub-bloques en la secci´n de sentencias de o un bloque.cantidad. Un /* inicia un .cantidad.1. CONSTANTES Y VARIABLES 3 BEGIN sentencias END. Las funciones y procedimientos disparadores no pueden iniciar o realizar transacciones y Postgres no soporta transacciones anidadas. Comentarios. a Normalmente una de las sentencias es el valor de retorno. usando la palabra clave RETURN. ı -. END. 5. el cual se extiende hasta el final de la l´ ınea.Creamos un sub-bloque -DECLARE cantidad INTEGER := 80. no solamente al ser o llamada la funci´n.Cantidad contiene aqu´ 30 ı cantidad := 50. Un doble gui´n – da inicio o a un comentario. ’ LANGUAGE ’plpgsql’. Por ejemplo: o CREATE FUNCTION estafunc() RETURNS INTEGER AS ’ DECLARE cantidad INTEGER := 30. ı -. BEGIN RAISE NOTICE ’’Cantidad contiene aqu´ %’’. Las variables declaradas en la secci´n que antecede a un bloque se inicializan o a su valor por omisi´n cada vez que se entra al bloque.cantidad. RAISE NOTICE ’’Cantidad contiene aqu´ %’’. Los sub-bloques pueden ser usados para ocultar las variables a los bloques m´s externos.

filas y registros usados en un bloque o en sus sub-bloques deben declararse en la secci´n de declaraciones del bloque. A continuaci´n se muestran algunos ejemplos de declaraci´n de variables: o o user_id INTEGER. $2. (el m´ximo es 16). o o Ejemplos: cantidad INTEGER := 32. 5. EL valor se eval´a cada vez que se llama la funci´n.2. CONSTANTES Y VARIABLES 4 bloque que se extiende hasta la primera ocurrencia de */.5 COMENTARIOS. todas las variables declaradas como NOT NULL deben contar con un valor por omisi´n espec´ o ıfico.1. El valor de una variable declarado como CONSTANT no puede ser modificado. El valor por omisi´n de todas la vario ables es el valor NULL de SQL. Puesto que el valor por omisi´n de todas las vriables o o es el valor NULL de SQL. no el de la hora en que se compil´ en bytecode. VARCHAR y CHAR. 5. quantity NUMBER(5). como INTEGER. url varchar := ’’http://misitio. user_id CONSTANT INTEGER := 10. la asignaci´n de un valor NULL causa en un error o en tiempo de ejecuci´n. Variables y constantes Todas las variables. 5.2. Constantes y variables con valores por omisi´n o La declaraciones tienen las siguiente sintaxis: nombre [ CONSTANT ] tipo [ NOT NULL ] [ { DEFAULT | := } valor ]. Variables pasadas a las funciones Las variables que se pasan a las funciones son denominadas con los identificadores $1. o La excepci´n es la variable de un ciclo FOR que itera sobre un rango de o valores enteros. Las variables en PL/pgSQL pueden ser de cualquier tipo de datos de SQL. Si acaso se especifica NOT NULL.com’’. a Algunos ejemplos: CREATE FUNCTION iva_venta(REAL) RETURNS REAL AS ’ DECLARE subtotal ALIAS FOR $1. as´ que asignar now a u o ı una variable de tipo timestamp causa que la variables almacene el tiempo real de la llamada a la funci´n.2. url VARCHAR. BEGIN . etc.2.

supongamos que usted tiene una columna llamada user id en la tabla users. ni los OID ni otros atributos del sistema (debido a que el rengl´n o o puede ser de una vista).15. BEGIN -. etc. lo que har´ es: ıa user_id users. %TYPE Proporciona el tipo de dato de una variable o una columna. ’ LANGUAGE ’plpgsql’. . es posible declarar variables con el mismo tipo de dato o estructura de otro item de la base de datos (por ejemplo. tabla puede ser una tabla o una vista que exista en la base de datos.INTEGER) RETURNS INTEGER AS ’ DECLARE v_string ALIAS FOR $1. a ı ı END. Los campos de un rowtype heredan los tama˜os n de los campos o la precisi´n de los tipos de dato para char(). Los campos del rengl´n se accesan con la notaci´n punto. pero debe usarse un seud´nimo o alias usando el a o comando ALIAS que se describe m´s adelante. 5 CREATE FUNCTION instr(VARCHAR. o o Los par´metros de una funci´n pueden ser de tipo compuesto (renglones a o completos de una tabla). Atributos Usando los atributos %TYPE and %ROWTYPE. o nombre tabla %ROWTYPE Declara una rengl´n con la estructura de la o tabla especificada. 6. END.Algunos c´culos ir´an aqu´. un campo de una tabla). Por ejemplo. o DECLARE users_rec users%ROWTYPE.6 ATRIBUTOS return subtotal * 1. ’ LANGUAGE ’plpgsql’. index ALIAS FOR $2. Es este caso.user_id\%TYPE. Se puede utilizar para declarar variables que almacenen valores de bases de datos. a Solamente los atributos del usuario de la tabla pueden ser accesibles en el rengl´n. Para declarar una variable con el mismo tipo de dato que el usado en nuestra tabla de usuarios. el identificador correspondiente $n ser´ del tipo rowtype. Al usar %TYPE puede despreocuparse de los cambios futuros en la definici´n de la tabla.

La columna mv_name de cs_materialized_views almacena -. IF NOT FOUND THEN RAISE EXCEPTION ’’View ’’ || key || ’’ not found’’.los nombres de las vistas. Expresiones Todas las expresiones usadas en las sentencias de PL/pgSQL son procesadas usando el ejecutor del backend. ’ LANGUAGE ’plpgsql’. BEGIN user_id := users_rec. 7. ..mv_name. o La unica excepci´n a esta regla es una sentencia EXECUTE si se requiere analizar ´ o una consulta cada vez que es encontrada.7 EXPRESIONES 6 user_id users%TYPE.mv_name || ’’ ’’ || table_data.mv\query. -. Todas las expresiones son evaluadas internamente ejecutando una sentencia SELECT expresi´n o usando el gestor de SPI. now para o o el tipo de dato timestamp) as´ que es imposible para el analizador sint´ctico ı a (parser ) de PL/pgSQL identificar los valores de las constantes reales diferentes de NULL.. Las expresiones que parecen contener constantes pueden requerir de una evaluaci´n en tiempo de ejecuci´n (por ejemplo. o En detalle. BEGIN SELECT INTO table_data * FROM cs_materialized_views WHERE sort_key=key. INSERT INTO table_data.user_id. En la expresi´n. las ocurrencias de los identificadores o de las variables se sustituyen por par´metros y los valores reales de las variables a se pasan al ejecutor en el arreglo de par´metros. TRUNCATE TABLE table_data. Todas las expresiones usadas a en una funci´n de PL/pgSQL son preparadas y almacenadas solamente una vez. table_data cs_materialized_views\%ROWTYPE. La revisi´n del tipo realizada por el analizador sint´ctico principal de Posto a gres tiene algunos efectos secundarios a la interpretaci´n de valores constantes. END IF. existe una diferencia entre lo que hacen estas dos funciones: CREATE FUNCTION logfunc1 (text) RETURNS timestamp AS ’ . return 1. end. create function cs_refresh_one_mv(integer) returns integer as ’ DECLARE key ALIAS FOR $1. RETURN 0.

END. ’ LANGUAGE ’plpgsql’.1. Asignaci´n o Una asignaci´n de un valor a una variable o campo de fila o de registro se o escribe: identifier := expression. END. al preparar el plan para el INSERT.8 SENTENCIAS 7 DECLARE logtxt ALIAS FOR $1. el o int´rprete de PL/pgSQL cambia esta cadena a un tipo de timestamp por medio e de una llamada a las funciones text out() and timestamp in() para efectuar la conversi´n. BEGIN curtime := ’’now’’. a u 8. As´ crear´ una conı. a stante a partir de ella en ese momento. ıa En el caso de logfunc2(). curtime timestamp. RETURN ’’now’’. . y por eso regresa un tipo de datos de texto. y la usar´ en cada una de las invocaciones a de logfunc1(). el analizador principal de Postgres conoce. RETURN curtime. En el caso de logfunc1(). y CREATE FUNCTION logfunc2 (text) RETURNS timestamp AS ’ DECLARE logtxt ALIAS FOR $1. el analizador principal de Postgres no sabe que tipo debe tener ’now’. ’’now’’). BEGIN INSERT INTO logtable VALUES (logtxt. curtime). Durante la asignaci´n a la variable local curtime. o 8. que la cadena ’now’ debe ser interpretada como timestamp debido a que el campo destino de logtable es de ese tipo. para su ejecuci´n. ’ LANGUAGE ’plpgsql’. Sentencias Cualquier cosa no comprendida por el analizador PL/pgSQL tal como se especifica adelante ser´ enviado al gestor de la base de datos. a o La consulta resultante no devolver´ ning´n dato. el cual contiene la cadena ’now’. INSERT INTO logtable VALUES (logtxt. mientras est´ vivo el backend. Es claro que esto no es lo que ese perar´ el programador.

agrupaci´n... Si el control o alcanza el fin del bloque de mayor nivel de la funci´n sin encontrar una sentencia o RETURN. que puede usarse inmediatamente despu´s de SELECT INTO para comprobar si una asignaci´n e o ha tenido ´xito. target puede ser un registro.8 SENTENCIAS 8 Si el tipo de dato resultante de la expresi´n no coincide con el tipo de dao to de las variables. . todas las dem´s se descartan. Si la selecci´n devuelve m´ltiples filas. Si una fila o una lista de variables se usa como objetivo. los valores seleccionados han de coincidir exactamente con la estructura de los objetivos o se producir´ un error de ejecuci´n. Abortando la ejecuci´n y mensajes o Use la sentencia RAISE para enviar mensajes al mecanismo elog de PostgreSQL.]]. que pueda pasarse a a o o una sentencia SELECT. La palabra clave FROM puede preceder a a o cualquier calificador v´lido.2. o o a El valor devuelto por una funci´n no puede quedar son definir. ocurrir´ un error de ejecuci´n. o la variable tienen un tama˜o o precisi´n conocido (con o mo char(29)). identifier [. IF NOT FOUND THEN RAISE EXCEPTION ’’employee % not found’’. Existe una variable especial llamada FOUND de tipo booleano.3. myname. END IF. Una asignaci´n de una selecci´n completa en un registro o fila puede hacerse o o del siguiente modo: SELECT expressions INTO target FROM . a 8.. solo la primera se mueve a los campos o u objetivo. o 8. tal como se ha descrito en el caso de las asignaciones. el resultado ser´ amoldado impl´ a ıcitamente por el interprete de bytecode de PL/pgSQL. una variable de fila o una lista separada por comas de variables y campo de de registros o filas. N´tese que esto o puede potencialmente producir errores de ejecuci´n generados por los tipos de o las funciones de entrada.. a o Las expresiones resultantes ser´n amoldadas autom´ticamente en los tipos a a devueltos por la funci´n. ordenaci´n. RAISE level ’format’ [. Volviendo de la funci´n o RETURN expresi´n o La funci´n termina y el valor de expresi´n se devolver´ al ejecutor superior. etc. e SELECT * INTO myrec FROM EMP WHERE empname = myname. usando los tipos de las variables para las funciones de entrada y los tipos resultantes en las funciones de salida..

a o a a 9.user_id. Esto abortar´ la transacci´n y escribir´ en la bit´cora de la base de datos. mediante una sentencia EXIT.v_job_id. 9.9 ESTRUCTURAS DE CONTROL DE FLUJO 9 Dentro del formato.2. ” %”se usa como comod´ para los subsecuentes identiın ficadores. NOTICE (escribe en la bit´cora de la base o a de datos y lo env´ a la aplicaci´n del cliente) y EXCEPTION (escribe en la ıa o bit´cora de la base de datos y aborta la transacci´n). [<<label>>] WHILE expression LOOP statements END LOOP. para especificar el nivel del bucle que ha de terminarse. Los posibles niveles son DEBUG (suprimido en las bases de datos de producci´n). v job id reeemplazar´ al % en la cadena ´ a RAISE EXCEPTION ’’Inexistent ID --> %’’. . a o RAISE NOTICE ’’Id number ’’ || key || ’’ not found!’’. Hay varios tipos de bucles. La etiqueta opcional puede ser usado por las sentencias EXIT de otros bucles anidados. En este ultimo ejemplo. 9. Se trata de un bucle no condicional que ha de ser terminado de forma explicita. expression debe devolver un valor que al menos pueda ser adaptado en un tipo booleano. RAISE NOTICE ’’Calling cs_create_job(%)’’. Estructuras de control de flujo Condiciones IF expression THEN statements [ELSE statements] END IF. Bucles [<<label>>] LOOP statements END LOOP.1. separados por comas.

Y es posible que la base de datos haya quedado en un estado a inconsistente. la transacci´n completa es abortada y el sistema vuelve al o lazo principal para procesar la siguiente consulta de la aplicaci´n cliente. E incluso aunque se pudiera enviar la informaci´n a la aplicaci´n cliente. Las dos expresiones dan el limite inferior y superior del rango y son evaluados s´lo cuando se entra en el bucle. La variable name se crea autom´ticamente con el tipo entero. Si se incluye label ha de ser la etiqueta del bucle actual u de otro de mayor nivel.. la transacci´n ya se abr´ o o o ıa abortado. un o error de an´lisis). EL bucle indicado se termina. Excepciones Postgres no dispone de un modelo de manejo de excepciones muy elaborado. y el control se pasa a la sentencia de despu´s del END del bucle o bloque correspondiente. expression LOOP statements END LOOP. El paso de la iteraci´n es siempre 1. por lo que carecer´ de sentido el intentar reanudar la operaci´n. . la ultima fila asignada es a´n accesible despu´s del u e bucle. Cuando el analizador. un error de punto flotante. [<<label>>] FOR name IN [ REVERSE ] express . y la sentencia se ejecuta para cada una de ellas. ıa o Por todo esto. Pero lo que no es posible es saber qu´ ha causado en realidad el e aborto (un error de conversi´n de entrada/salida. y existe solo dentro a del bucle. Se trata de un bucle que se itera sobre un rango de valores enteros. EXIT [ label ] [ WHEN expression ]. lo unico que hace PL/pgSQL cuando se produce un aborto ´ de ejecuci´n durante la ejecuci´n de una funci´n o procedimiento disparador o o o es enviar mensajes de depuraci´n al nivel DEBUG. y se ejecuta la sentencia a que sigue a END LOOP.9 ESTRUCTURAS DE CONTROL DE FLUJO 10 Se trata de un lazo condicional que se ejecuta mientras la evaluaci´n de o expression sea cierta. se termina el lazo m´s interno. El registro o fila se asigna a todas las filas resultantes de la clausula de selecci´n. Si el bucle se termina o con una sentencia EXIT. el optimizador o el ejecutor deciden que una sentencia no puede ser procesada. o o [<<label>>] FOR record | row IN select_clause LOOP statements END LOOP. Si no se incluye label. o Es posible introducirse en el mecanismo de errores para detectar cuando sucede esto. por lo que volver a un nivel de ejecuci´n superior o continuar o ejecutando comandos puede corromper toda la base de datos. indicando en qu´ funci´n y o e o donde (numero de l´ ınea y tipo de sentencia) ha sucedido el error.3. e 9.

Ejemplos Se incluyen unas pocas funciones para demostrar lo f´cil que es escribir a funciones en PL/pgSQL. . BEGIN IF emprec. CREATE FUNCTION concat_text (text. A´n estamos u trabajando en una alternativa m´s elegante.1. END. Algunas funciones sencillas en PL/pgSQL Las dos funciones siguientes son id´nticas a sus contrapartidas que se ver´n e a cuando estudiemos el lenguaje C. sallim ALIAS FOR $2. estas funciones PL/pgSQL tendr´n su equivalente en lenguaje C. Funciones PL/pgSQL para tipos compuestos De nuevo. ’ LANGUAGE ’plpgsql’.salary ISNULL THEN RETURN ’’f’’. END.salary > sallim. El texto de las funciones en CREATE FUNCTION ha de ser una cadena de texto. text) RETURNS text AS ’ BEGIN RETURN $1 || $2. duplique las comila las sencillas como en los ejemplos siguientes.10 EJEMPLOS 11 10. ’ LANGUAGE ’plpgsql’. END IF. CREATE FUNCTION add_one (int4) RETURNS int4 AS ’ BEGIN RETURN $1 + 1. Mientras tanto. int4) RETURNS bool AS ’ DECLARE emprec ALIAS FOR $1. o Un detalle doloroso a la hora de escribir funciones en PL/pgSQL es el manejo de la comilla simple. el programador deber´ a ıa consultar el test de regresi´n de PL/pgSQL. Para ejemplos m´s complejos. Las comillas simples en el interior de una cadena literal deben de duplicarse o anteponerse de una barra invertida. ’ LANGUAGE ’plpgsql’. Cualquier soluci´n a este problema o en futuras versiones de Postgres mantendr´n la compatibilidad con esto. a 10. END. a CREATE FUNCTION c_overpaid (EMP. RETURN emprec.

END IF.postgresql. NEW.empname ISNULL THEN RAISE EXCEPTION ’’empname cannot be NULL value’’.last_date := ’’now’’. .empname.html Oracle 8. cada vez que se inserte o actualice un fila en la tabla.11 REFERENCIAS 12 10. last_user name). Corey. ıa Cap´ ıtulo 7: PL/SQL. Gu´ de aprendizaje. Y asegura que se proporciona un nombre de empleado y que el salario tiene un valor positivo.2.salary ISNULL THEN RAISE EXCEPTION ’’% cannot have NULL salary’’. Referencias PL/pgSQL: http://lucas. END IF.empname. END IF. last_date datetime.salary < 0 THEN RAISE EXCEPTION ’’% cannot have a negative salary’’. IF NEW.php?programmer-pl. CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp FOR EACH ROW EXECUTE PROCEDURE emp_stamp(). NEW.Remember who changed the payroll when NEW. CREATE TABLE emp ( empname text. ’ LANGUAGE ’plpgsql’. NEW.Check that empname and salary are given IF NEW. CREATE FUNCTION emp_stamp () RETURNS OPAQUE AS BEGIN -.last_user := getpgusername().hispalinux. 11.Who works for us when she must pay for? IF NEW. END. -. Procedimientos desencadenados en PL/pgSQL Estos procedimientos desencadenados aseguran que. Osborne.es/Postgresql-es/web/navegable/programmer/x1503. Michael Abbey y Michael J.html Procedural Languages: http://www. salary int4.org/idocs/index. se incluya el nombre del usuario y la fecha y hora. -. RETURN NEW.

Sign up to vote on this title
UsefulNot useful