PL Gestión y Modelación de Datos PL ● ● ● ● ● PL/pgSQL es un lenguaje de programación procedimental Permite crear funciones en la base de datos PostgreSQL Las funciones agrupan secuencias de sentencias SQL y sentencias de programación Reduce la carga de comunicación entre el cliente y la base de datos Mejora el performance de los programas PL – CREATE FUNCTION CREATE OR REPLACE FUNCTION identifier (arguments) RETURNS type AS ' DECLARE declaration; [...] BEGIN statement; [...] END; ' LANGUAGE 'plpgsql'; DROP FUNCTION [ IF EXISTS ] name (arguments) [ CASCADE | RESTRICT ] PL – CREATE FUNCTION CREATE OR REPLACE FUNCTION cuadrado (integer) RETURNS integer AS ' DECLARE cuad integer; BEGIN cuad = $1 * $1; RETURN cuad; END; ' LANGUAGE 'plpgsql'; postgres=> \i cuadrado.sql CREATE FUNCTION postgres=> select cuadrado (10); PL – CREATE FUNCTION ● ● ● ● ● No se puede usar REPLACE para cambiar el nombre o tipo de los argumentos, ni el tipo de retorno ● Permite sobrecarga del nombre de la función Los argumentos tienen un modo (IN, OUT, INOUT), un nombre y un tipo ● IN por defecto El nombre del lenguaje puede ser también C, internal Cuando se borra la función en cascada automáticamente se borran los objetos que dependen de ella (ej. Triggers) Comentarios: - - comentario línea /* comentario de Bloque */ PL – DECLARE ● ● Alias para los argumentos Variables variable_name [ CONSTANT ] data_type [ NOT NULL ] [ { DEFAULT | := } value ]; Ejemplo: CREATE OR REPLACE FUNCTION AgregarPais (CHAR(2), VARCHAR(40), TEXT ) RETURNS boolean AS ' DECLARE -- Alias para los argumentos codigo ALIAS FOR $1; nombre ALIAS FOR $2; region ALIAS FOR $3; -- Variables cod_region SMALLINT; PL – STATEMENTS ● ● ● ● ● ● ● ● ● Asignación (:=) SELECT INTO IF – THEN – ELSE – END IF LOOP – END LOOP WHILE – LOOP – END LOOP FOR – IN – LOOP – END LOOP RAISE Llamado a Funciones RETURN PL – STATEMENTS CREATE OR REPLACE FUNCTION AgregarRegion (varchar(40)) RETURNS timestamp AS ' -- Agrega una region siguiendo el consecutivo del código DECLARE -- Alias para los argumentos nombre ALIAS FOR $1; -- Variables codigo SMALLINT; hora TIMESTAMP; BEGIN hora := ''now''; SELECT INTO codigo MAX(cod_region) FROM region ; -- Calcula nuevo codigo (anterior + 1) codigo := codigo + 1; -- Inserta la nueva region INSERT INTO region VALUES (codigo, nombre, hora); RETURN hora; END; ' LANGUAGE 'plpgsql'; PL – STATEMENTS - timestamp CREATE OR REPLACE FUNCTION AgregarRegionBAD (text) RETURNS boolean AS ' -- Agrega una region siguiendo el consecutivo del código DECLARE -- Alias para los argumentos nombre ALIAS FOR $1; -- Variables codigo SMALLINT; -- hora TIMESTAMP; BEGIN -- hora := ''now''; SELECT INTO codigo cod_region FROM region ORDER BY cod_region DESC; -- Calcula nuevo codigo (anterior + 1) codigo := codigo + 1; -- Inserta la nueva region INSERT INTO region VALUES (codigo, nombre, ''now''); RETURN TRUE; END; ' LANGUAGE 'plpgsql'; PL – STATEMENTS CREATE OR REPLACE FUNCTION AgregarRegionALT (varchar(40)) RETURNS timestamp AS ' -- Agrega una region siguiendo el consecutivo del código DECLARE -- Alias para los argumentos nombre ALIAS FOR $1; -- Variables codigo SMALLINT; hora TIMESTAMP; BEGIN hora := ''now''; SELECT codigo cod_region FROM region ORDER BY cod_region DESC ; -- Calcula nuevo codigo (anterior + 1) codigo := codigo + 1; -- Inserta la nueva region INSERT INTO region VALUES (codigo, nombre, hora); RETURN hora; END; ' LANGUAGE 'plpgsql''; PL – IF – THEN – ELSE – END IF CREATE FUNCTION identifier (arguments) RETURNS type AS ' DECLARE declarations BEGIN IF condition THEN Statement; […] [ ELSE statement; […] ] END IF; END; ' LANGUAGE 'plpgsql'; PL – IF – THEN – ELSE – END IF CREATE OR REPLACE FUNCTION BuscarNombreOApellido (TEXT, TEXT) RETURNS TEXT AS ' -- Busca si hay un empleado con el nombre o el apellido dados DECLARE apellido TEXT; nombre TEXT; BEGIN SELECT INTO nombre, apellido nombres, apellidos FROM empleado WHERE nombres = $1 OR apellidos = $2; IF NOT FOUND THEN RETURN ''No encontro''; ELSE IF nombre = $1 THEN RETURN ''Encontro Nombre''; ELSE RETURN ''Encontro Apellido''; END IF; END IF; END; ' LANGUAGE 'plpgsql'; PL – LOOP – END LOOP [ <<label>> ] LOOP statement; [...] EXIT [ label ] [ WHEN condition ]; END LOOP; ● <<label>> permite especificar a que LOOP se refiere un EXIT, cuando hay loops anidados PL – WHILE – END LOOP [ <<label>> ] WHILE condition LOOP statement; [...] END LOOP; PL – FOR – END LOOP [ <<label>> ] FOR identifier IN [ REVERSE ] expression1 .. expression2 LOOP statement; [...] END LOOP; [ <<label>> ] FOR { record_variable | %rowtype_variable } IN select_statement LOOP statement; [...] END LOOP; PL – FOR – IN – END LOOP CREATE OR REPLACE FUNCTION NombreApellido () RETURNS TEXT AS ' -- Busca si hay un empleado con el nombre o el apellido dados DECLARE nombreyApellido TEXT; textOutput TEXT := E'' ''; BEGIN FOR nombreyApellido IN SELECT nombres||'' ''||apellidos FROM empleado ORDER BY nombres, apellidos LOOP textOutput := textOutput || nombreyApellido||E''\n''; END LOOP; RETURN textOutput; END; ' LANGUAGE 'plpgsql'; PL – FOR – ROWTYPE CREATE OR REPLACE FUNCTION NombreApellidoROW () RETURNS TEXT AS ' -- Retorna nombres y apellidos de los empleados DECLARE rowData empleado%ROWTYPE; textOutput TEXT := '' ''; BEGIN FOR rowData IN SELECT * FROM empleado ORDER BY nombres, apellidos LOOP textOutput := textOutput || rowData.nombres || rowData.apellidos||E''\n''; END LOOP; RETURN textOutput; END; ' LANGUAGE 'plpgsql'; PL – RAISE RAISE level ''message string'' [, identifier [...] ]; ● ● Levels: ● DEBUG: envia el mensaje al log, y al cliente si la BD esta en modo depuración ● NOTICE: envia el mensaje al log y al cliente ● EXCEPTION: envia el mensaje al log y al cliente, y aborta la ejecución Identifier: nombre de las variables que va a incluir en el mensaje (debe marcarse con % en el texto) PL – RAISE CREATE OR REPLACE FUNCTION RaiseError () RETURNS boolean AS ' DECLARE var1 integer; var2 integer; BEGIN var1 := 10; var2 := 15; RAISE EXCEPTION ''Error en % y en %'', var1, var2; END; ' LANGUAGE 'plpgsql'; PL – Llamado a Funciones SELECT function_identifier(arguments); variable_identifier := function_identifier(arguments); PERFORM function_identifier(arguments); Ejercicio ● Con la base de datos de empleados: ● ● Escriba una función que busque los máximos jefes (jefes que no tiene jefe), y retorne un conjunto de registros tipo texto donde los nombres de cada jefe esta seguido por los nombres de sus subalternos inmediatos Escriba una función que retorne una cadena en la que los jefes máximos aparecen con nivel 0, sus subalternos con nivel 1, y los subalternos de ellos con nivel 2, y así sucesivamente