PL/PGSQL
PL/PGSQL
Pero primero triggers
Pero primero triggers
DROP TABLE producto cascade;CREATE TABLE producto( id int PRIMARY KEY, vendido timestamp); DROP FUNCTION modificacion() cascade;
CREATE FUNCTION modificacion() RETURNS TRIGGER AS ' BEGIN
NEW.vendido := ''now''; RETURN NEW;
END;
'LANGUAGE 'plpgsql';
DROP TRIGGER t_modificacion on producto;
CREATE TRIGGER t_modificacion BEFORE INSERT ON producto FOR EACH ROW EXECUTE PROCEDURE modificacion();
INSERT into producto VALUES (1); SELECT * FROM producto;
Trigger Layout
CREATE TRIGGER <trigger_name>
BEFORE AFTER INSERT UPDATE DELETE OR ON <table_name>
FOR EACH ROW
FOR EACH STATEMENT
EXECUTE PROCEDURE <function_name> ( <arguments> );
Extensiones SQL-99
Extensiones SQL-99
■
Entre las propuestas definidas en el estándar SQL-99
están la extensión del SQL dotándolo de comandos
para controlar el flujo:
★
If, where, loop, etc
■
El standard SQL-99 esta bastante lejos de ser
satisfecho por la mayoría de las bases de datos.
■
Oracle tiene una versión propia de estas extensiones
"procedurales" llamada
PL/SQL
Por qué PL/pgSQL
Por qué PL/pgSQL
■
Permite crear funciones que se ejecutan en el
servidor (versus otras aproximaciones como JDBC
que se ejecutan en el cliente con "overhead" de
comunicaciones).
■
La propia base de datos se encarga de compilar y
gestionar estas funciones con lo que suelen ser
eficientes.
■
proporciona:
★ variables ★ bucles
Un paso previo a usar PL/pgSQL
Un paso previo a usar PL/pgSQL
■ Cuando se crea una base de datos nueva hace falta "autorizar"
el uso de pl/pgSQL (a menos que template1 ya este autorizada)
createdb mydatab
createlang plpgsql mydatab
■ En los laboratorios debería estar "autorizado" por defecto
■ Ojo: no se comprueba la sintaxis de las funciones hasta que no
PL/pgSQL: Estructura de las
PL/pgSQL: Estructura de las
funciones
funciones
■ PL/pgSQL presenta una estructura en "Bloques". ■ Cada bloque se define usando
DECLARE
--variables inicializada con NULL cada vez --que se entra en el bloque
[...]
BEGIN
--comandos; [...]
END;
PL/pgSQL estructura
PL/pgSQL estructura
CREATE FUNCTION nombre_función (argumentos) RETURNS type AS ' DECLARE declaracion; --variables [...] BEGIN statement; --comandos [...] END; ' LANGUAGE 'plpgsql';
■ Una función puede constar de varios bloques y estos pueden
PL/pgSQL: Generalidades
PL/pgSQL: Generalidades
■ Los tipos de datos pasados a la función se dan en paréntesis
(sin nombre de variable antes de postgres 8)
■ El cuerpo de la función se pasa a la base de datos como una
cadena de caracteres (nótese, que el cuerpo empieza y acaba con comillas simples) (escape doble)
■ Tras la cadena el lenguaje usado para crear la función se define
usando la orden "LANGUAJE" (otros lenguajes posibles son PL/PERL, PL/TCL, C, etc)
Ejemplo trivial sin pasar parámetros
Ejemplo trivial sin pasar parámetros
■ ¿Qué hace esta función?
CREATE OR REPLACE FUNCTION una_funcion () RETURNS int4 AS
' DECLARE
an_integer int4; --variables BEGIN an_integer := 10 * 10; --comandos RETURN an_integer; END; ' LANGUAGE 'plpgsql'; ____________________________ select una_funcion(); una_funcion 100 (1 row)
Tipos de Variables-I
Tipos de Variables-I
■ Ejemplos de variables: id_usuario INTEGER; cantidad NUMERIC(5,2); url VARCHAR;-- Mas sobre los tipos siguientes más adelante micampo mitabla.campo%TYPE;
mitupla mitabla%ROWTYPE;
■ The general syntax of a variable declaration is:
Tipos de Variables II
Tipos de Variables II
■ Todos los tipos de variable definidos para SQL son válidos en
PL/pgSQL
■ No es imprescindible conocer el tipo de variables de los atributos
★ Ejemplos usando %TYPE
DECLARE … mivar payroll.salario%TYPE; BEGIN … RETURN mivar*2;
★ %ROWTYPE reserva sitio para toda la tupla ★ Reteniendo la estructura de los datos
DECLARE … mivar payroll%ROWTYPE; BEGIN … RETURN mivar.salario*2;
Ejemplo trivial pasando variables
Ejemplo trivial pasando variables
CREATE OR REPLACE FUNCTION cal_longitud (text) RETURNS int4 AS
' DECLARE
intext ALIAS FOR $1; --primer parametro resultado int4;
BEGIN
resultado := (SELECT LENGTH(intext)); RETURN resultado; END; ' LANGUAGE 'plpgsql'; ____________________________ SELECT cal_longitud('qwerty'); cal_longitud
Más sobre Variables
Más sobre Variables
■ CREATE FUNCTION mifuncion(INTEGER, CHAR, …) ■ Se pueden pasar hasta 16 variables
★ $1, $2, …, $16
■ ALIAS permite renombrar variables
CREATE FUNCTION cal_longitud (text) RETURNS int4 AS
' DECLARE
intext ALIAS FOR $1; --primer parametro resultado int4;
Ejemplo usando Rowtype
Ejemplo usando Rowtype
CREATE OR REPLACE FUNCTION trae_pelicula (integer) RETURNS text AS '
DECLARE
pelicula_id ALIAS FOR $1;
encontrada_pelicula pelicula%ROWTYPE; BEGIN
SELECT INTO encontrada_pelicula * FROM pelicula WHERE id = pelicula_id;
RETURN encontrada_pelicula.titulo || '' ('' || encontrada_pelicula.agno || '')'';
END;
' LANGUAGE 'plpgsql';
Ejercicio
Ejercicio
■ En la base de datos de películas crear dos funciones que nos
sirvan para llenar la tabla reparto, dando el nombre de la pelicula y el nombre del actor
CREATE TABLE PELICULA(
ID INTEGER, -- Identificador único TITULO CHAR(70), -- Titulo de la película AGNO DECIMAL(4), -- Año de estreno
PUNTUACION FLOAT, -- Puntuación media VOTOS INTEGER, -- Numero de votos PRIMARY KEY (ID));
--CREATE TABLE ACTOR (
ID INTEGER, -- Identificador Único NOMBRE CHAR(35), -- Nombre del actor/actriz PRIMARY KEY (ID));
--CREATE TABLE REPARTO(
PELICULA_ID INTEGER, -- referencia a la tabla PELICULA ACTOR_ID INTEGER, -- referencia a la tabla ACTOR_ID ORD INTEGER, -- Orden en el reparto
-- La estrella es 1, ...
--FOREIGN KEY (PELICULA_ID ) REFERENCES PELICULA(ID), FOREIGN KEY (ACTOR_ID) REFERENCES ACTOR(ID),
Control de Flujo
Control de Flujo
■ Los programas no suelen ejecutarse de principio a fin sin
exceptuar ninguna línea de código. PL/pgSQL contiene
estructuras de control que permiten seleccionar las líneas de código que serán ejecutarse en tiempo real.
■ IF…THEN…ELSE…ELSE IF ★ ejecución condicional
■ LOOPS, WHILE LOOPS, FOR LOOPS ★ iteraciones
Ejemplo IF/ELSE
Ejemplo IF/ELSE
■ Programa que calcula la longitud de dos cadenas y devuelve la
longitud mayor.
CREATE OR REPLACE FUNCTION cadena_mas_larga(text, text) RETURNS int4 AS '
DECLARE
in_uno ALIAS FOR $1; in_dos ALIAS FOR $2; lon_uno int4;
lon_dos int4; result int4; BEGIN
lon_uno := (SELECT LENGTH(in_uno)); lon_dos := (SELECT LENGTH(in_dos));
IF lon_uno > lon_dos THEN RETURN lon_uno; ELSE RETURN lon_dos;
END IF; END;
'LANGUAGE 'plpgsql';
■ NOTA 1: se pueden hacer condiciones mas
complicadas usando OR y AND
■ NOTA 2: Como PL/pgSQL se agrupa en bloques no
Ejemplo bucle WHILE
Ejemplo bucle WHILE
(FOR)(FOR)■ Función que cuenta cuantas veces aparece un carácter en una cadena CREATE OR REPLACE FUNCTION cuentac(text,text) RETURNS
INT4 AS ' DECLARE
intext ALIAS FOR $1; inchar ALIAS FOR $2; lon int4; resultado int4;
i int4; tmp char; BEGIN
lon := length(intext)+1; i:=1; resultado:=0;
WHILE i <= lon LOOP
tmp := substr(intext,i,1); IF tmp = inchar THEN resultado := resultado +1; END IF; i:=i+1; END LOOP; RETURN resultado;
Excepciones
Excepciones
■ RAISE se usa para imprimir mensajes y, en el caso de
excepcion, abortar la transacción
■ RAISE { NOTICE | EXCEPTION} ■ RAISE NOTICE
★ RAISE NOTICE ' No hagas eso!' ';
★ RAISE NOTICE ' 'El señor' ' || id || ' 'no está en casa' '; ★ RAISE NOTICE ' 'el señor % no está en casa' ' , id;
Excepciones: Ejemplo
Excepciones: Ejemplo
■ Calcular la suma de los enteros de n a m (usar la formula (p+1)*p/2
CREATE OR REPLACE FUNCTION suma(int4, int4) RETURNS int4 AS ' DECLARE
inicio ALIAS FOR $1; fin ALIAS FOR $2; resultado int;
BEGIN
IF (inicio <1) THEN
RAISE EXCEPTION ''inicio debe ser mayor que 1''; ELSE
IF(inicio <= fin) THEN
resultado := (fin+1)*fin/2 (inicio-1)*inicio/2; ELSE
RAISE EXCEPTION ''El valor inicial % debe ser menor que el final %'', inicio, fin;
END IF; END IF;
RETURN resultado; END
SELECT y Bucles
SELECT y Bucles
CREATE OR REPLACE FUNCTION trae_pelicula (integer) RETURNS text AS '
DECLARE
pelicula_id ALIAS FOR $1;
encontrada_pelicula pelicula%ROWTYPE; BEGIN
SELECT INTO encontrada_pelicula * FROM pelicula WHERE id = pelicula_id;
RETURN encontrada_pelicula.titulo || '' ('' || encontrada_pelicula.agno || '')'';
■ Nota: Si SELECT INTO devuelve más de una tupla se ignoran
SELECT y Bucles
SELECT y Bucles
■ Cuantas tuplas empiezan con una letra determinada
CREATE OR REPLACE FUNCTION cuenta_letra (text) RETURNS int4 AS '
DECLARE
caracter ALIAS FOR $1; temporal record; tmp_caracter text; resultado int4;
BEGIN
resultado:=0;
FOR temporal IN SELECT titulo FROM pelicula LOOP tmp_caracter :=substr(temporal.titulo,1,1);
IF tmp_caracter = caracter THEN resultado := resultado +1; END IF;
END LOOP;
RETURN resultado; END;
Examen Parcial Nov
PERSONA OBJETO PUJA Subastado por Persona Puja Puja Objeto• Rechazar pujas no admisibles • 10% menos que puja anterior • Un día más tarde
• Actualizar campos redundantes • vendido
TABLAS
TABLAS
CREATE TABLE persona(
id SERIAL, --identificador unico PRIMARY KEY(id)
);
CREATE TABLE objeto(
id SERIAL, --identificador unico persona_id INT,
fecha_salida TIMESTAMP DEFAULT now(),
vendido int DEFAULT 0, -- 1 vendido, 0 no vendido precio_salida NUMERIC(10,2),
PRIMARY KEY(id),
FOREIGN KEY (persona_id) REFERENCES persona(id) );
CREATE TABLE puja( objeto_id INT,
persona_id INT, fecha TIMESTAMP, cuantia NUMERIC(10,2),
DATOS
DATOS
--poblar la tabla ----persona (id)--INSERT INTO persona VALUES (1);--INSERT INTO persona VALUES (2);--INSERT INTO persona VALUES (3);--INSERT INTO persona VALUES (4);--INSERT INTO persona VALUES (5);
----objeto(id, persona_id,fecha_salida,vendido,precio_salida)
--INSERT INTO objeto VALUES (1,1,now(),DEFAULT,23);--INSERT INTO objeto VALUES (2,1,now()+'-1.1 day',DEFAULT,23); INSERT INTO objeto VALUES (3,3,now()+'-3.14 day',DEFAULT,23); INSERT INTO objeto VALUES (4,3,now()+'-2.9 day',DEFAULT,23); INSERT INTO objeto VALUES (5,3,now()+'-5.9 day',DEFAULT,23); INSERT INTO objeto VALUES (6,4,now()+'-5.9 day',DEFAULT,23); INSERT INTO objeto VALUES (7,4,now()+'-0.9 day',DEFAULT,23); INSERT INTO objeto VALUES (8,5,now()+'-0.8 day',DEFAULT,23); INSERT INTO objeto VALUES (9,5,now()+'-1.34 day',DEFAULT,23); INSERT INTO objeto VALUES (10,5,now()+'-5.9 day',DEFAULT,23);
----puja (objeto_id,persona_id,fecha)
--INSERT INTO puja VALUES (1,1,now()+'1 hour',10.34); INSERT INTO puja VALUES (1,1,now()+'2 hour',10.34); INSERT INTO puja VALUES (1,1,now()+'3 hour',10.34); INSERT INTO puja VALUES (1,1,now()+'4 hour',10.34); INSERT INTO puja VALUES (1,1,now()+'5 hour',10.34); INSERT INTO puja VALUES (1,1,now()+'10 hour',10.34); INSERT INTO puja VALUES (10,1,now()+'1 hour',10.34); INSERT INTO puja VALUES (9,1,now()+'1 hour',10.34);
Ejemplo función en C
Ejemplo de Función en C
Ejemplo de Función en C
■ Código #include "postgres.h" #include <string.h> /* by value */int add_one(int arg) {
return arg + 1; }
■ Compilación
gcc -fpic -c foo.c -I /usr/include/pgsql/server/ gcc -shared -o foo.so foo.o
Ejemplo de Función en C-II
Ejemplo de Función en C-II
■ Instalacion:
★ como istrador de la base (postges):
CREATE FUNCTION add_one(integer) RETURNS integer AS '/tmp/foo.so', 'add_one'
LANGUAGE C STRICT;
_______________________________
■ add_one.so es la librería dinámica con la función ■ Más información en:
http://developer.postgresql.org/docs/postgres/xfunc-c.html
■ Recordar que C no soporta todos los tipos usados