Roberto Mello
3a. Conferncia Brasileira de PostgreSQL - PGCon-BR Campinas - 2009
Objetivos
Apresentar as partes principais do PL/pgSQL Apresentar casos de uso comum do PL/pgSQL Apresentar alguns casos do que NO fazer com PL/ pgSQL Apresentar os diferentes tipos de gatilhos Como criar gatilhos com PL/pgSQL Exerccios para prtica Resolver dvidas (ou pelo menos tentar)
2
Modelo de Dados
CREATE TABLE vendedores( vendedor_id SERIAL NOT NULL PRIMARY KEY, nome varchar(256)); INSERT INTO vendedores VALUES (DEFAULT, Fabio), (DEFAULT, Joao), (DEFAULT, Leonardo); CREATE TABLE vendas( item_id integer, vendedor_id integer REFERENCES vendedores.vendedor_id, quantidade integer, preco numeric); INSERT INTO vendas VALUES (1, 1, 5, 142), (2, 3, 54, 45), (3, 2, 21, 467), (4, 2, 32, 343), (5, 1, 21, 235);;
PL/pgSQL
uma linguagem de procedimentos (mas no a nica aceita no PostgreSQL) Adiciona estrutura e controle ao SQL parecida com outras linguagens da famlia C Roda dentro do servidor, evitando a viagem e converses entre cliente e servidor Um dos seus principais usos em gatilhos Acesso todas as funcionalidades do SQL
5
Estrutura
CREATE OR REPLACE FUNCTION uma_funcao(preco numeric) RETURNS integer AS $$ DECLARE -- declaraes de variveis quantidade integer := 30; BEGIN -- expresses, consultas, clculos, etc RETURN quantidade * preco; END; $$ LANGUAGE plpgsql;
Parmetros
Podem ser de entrada (IN) ou sada (OUT)
CREATE OR REPLACE FUNCTION calc_item(p_item_id int, OUT quantidade int, OUT total numeric) AS $$ BEGIN SELECT vendas.quantidade, vendas.quantidade * vendas.preco INTO quantidade, total FROM vendas WHERE item_id = p_item_id; END; $$ LANGUAGE plpgsql;
ltimos valores atribudos s variveis de sada so os que valem Uso de RETURN seria redundante
Blocos
CREATE OR REPLACE FUNCTION uma_funcao() RETURNS integer AS $$ << fora >> DECLARE quantidade integer := 30; BEGIN RAISE NOTICE 'Quantidade aqui %', quantidade; -- 30 quantidade := 50; --- Criamos um sub-bloco -DECLARE quantidade integer := 80; BEGIN RAISE NOTICE 'Quantidade aqui %', quantidade; -- 80 RAISE NOTICE 'Quantidade de fora aqui %', fora.quantidade; END; RAISE NOTICE 'Quantidade aqui %', quantidade; RETURN quantidade; END; $$ LANGUAGE plpgsql; -- 50
-- 50
Sobre transaes
Uma funo PL/pgSQL e todos os seus blocos roda INTEIRAMENTE dentro de uma transao BEGIN e END dentro de uma funo so para delinear os blocos, e no para controlar transaes Dentro da funo no h como alterar a transao da funo (e.g. COMMIT/ROLLBACK) Pode-se criar sub-transaes dentro da funo usando controle de excees (a ser visto mais adiante)
9
Operadores
variavel := expressao expressao ser avaliada como SQL e deve retornar um valor escalar *, +, -, / Qualquer outro operador permitido pelo mecanismo de SQL
10
Exerccio 1
Crie uma funo que aceite 2 nmeros inteiros e retorne a soma dos dois nmeros
11
Executando Consultas
SQL que no retorna registros pode ser executado diretamente.
CREATE FUNCTION remarca( p_item_id int, p_pct int) RETURNS NULL AS $$ DECLARE novo_preco numeric DEFAULT 0; BEGIN UPDATE vendas SET preco = preco + 1 WHERE item_id = p_item_id; -- Usando PERFORM ao invs de SELECT descartar todos -- os resultados que sejam retornados PERFORM remarca_todos(); -- Varivel especial FOUND IF FOUND THEN raise notice resultados descartados; END IF; END; $$ LANGUAGE plpgsql;
12
Executando Consultas
Resultados de consultas precisam ser postos em variveis
CREATE FUNCTION remarca( p_item_id int, p_pct int) RETURNS numeric AS $$ DECLARE novo_preco numeric DEFAULT 0; BEGIN UPDATE vendas SET preco = preco + (preco * 1/p_pct) WHERE item_id = p_item_id RETURNING preco INTO novo_preco; RETURN novo_preco; END; $$ LANGUAGE plpgsql;
13
Executando Consultas
Do mesmo jeito para SELECT, INSERT, DELETE
SELECT expresses_select INTO [STRICT] alvo FROM ...; INSERT ... RETURNING expresses INTO [STRICT] alvo; UPDATE ... RETURNING expresses INTO [STRICT] target; DELETE ... RETURNING expresses INTO [STRICT] alvo;
14
Exerccio 2
Crie uma funo que: Receba um identicador de um vendedor Retorne a soma das vendas daquele vendedor
15
Registros
Variveis do tipo RECORD armazenam registros inteiros DECLARE uma_venda RECORD; BEGIN SELECT * INTO uma_venda FROM vendas WHERE item_id = p_item_id; RAISE NOTICE Preco: %,uma_venda.preco;
16
Estruturas de Controle
IF var <> 0 THEN faa algo ELSIF var >= 2 THEN faa outra coisa ELSE trate casos extras END IF; WHILE preco > 410 LOOP RAISE NOTICE Bom Lucro; END LOOP; CASE vendedor WHEN 1, 3 THEN faca algo aqui ELSE outros calculos END CASE; CASE WHEN vendedor = 1 THEN comissao := 100; WHEN vendedor = 2 THEN comissao := 60; ELSE comissao := 30; END CASE;
17
18
Exerccio 3
Crie uma funo que receba um vendedor_id e imprima Parabens! para cada venda (tabela vendas) que o total (preco * quantidade) da venda tenha passado de 800
19
RETURNS TABLE o mesmo que declarar vrios parmetros OUT e especicar RETURNS SETOF umtipo
20
21
22
Cursores
Cursores so resultados de consultas que se pode ir e voltar na lista dos resultados, ao invs de s adiante So ecientes no que diz respeito ao uso de memria S podem ser usados dentro de uma transao Operaes:
FETCH [ direction { FROM | IN } ] cursor INTO target; MOVE [ direction { FROM | IN } ] cursor; UPDATE table SET ... WHERE CURRENT OF cursor; DELETE FROM table WHERE CURRENT OF cursor;
23
Cursores
CREATE FUNCTION myfunc(refcursor, refcursor) RETURNS SETOF refcursor AS $$ BEGIN OPEN $1 FOR SELECT * FROM table_1; RETURN NEXT $1; OPEN $2 FOR SELECT * FROM table_2; RETURN NEXT $2; END; $$ LANGUAGE plpgsql; -- precisa estar numa transao para usar cursores. BEGIN; SELECT * FROM myfunc('a', 'b'); FETCH ALL FROM a; FETCH ALL FROM b; COMMIT;
24
Gatilhos
Funes que so chamadas automaticamente antes (before) ou depois (after) de Insert, Update, Delete, Truncate Funo tem que ser declarada especcamente como de gatilho, e um CREATE TRIGGER tem que ser rodado por tabela sobre a qual o gatilho incidir
25
Gatilhos - variveis
NEW - armazena o novo registro em insert/update OLD - armazena o registro velho em UPDATE/DELETE TG_NAME - nome do gatilho disparado TG_WHEN - string contendo BEFORE ou AFTER TG_LEVEL - string com ROW ou STATEMENT dependendo do gatilho TG_OP - string com INSERT, UPDATE, DELETE, or TRUNCATE indicando a operao TG_RELID - oid da tabela que chamou o gatilho TG_TABLE_NAME - nome da tabela que chaou o gatilho TG_TABLE_SCHEMA - nome do esquema da tabela que chamou o gatilho TG_NARGS - nmero de argumentos dados funo do gatilho no CREATE TRIGGER TG_ARGV[] - matriz com os argumentos dados no CREATE TRIGGER
26
Gatilhos
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$ BEGIN IF NEW.empname IS NULL THEN RAISE EXCEPTION 'empname no pode ser nulo'; END IF; IF NEW.salary IS NULL THEN RAISE EXCEPTION '% no pode ter salrio nulo', NEW.empname; END IF; IF NEW.salary < 0 THEN RAISE EXCEPTION '% no pode ter salrio negativo', NEW.empname; END IF; -- Lembre quem mudou a folha, e quando NEW.last_date := current_timestamp; NEW.last_user := current_user; RETURN NEW; END; $emp_stamp$ LANGUAGE plpgsql; CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
27
Exerccio 4
Crie uma funo de gatilho que insira um novo registro na tabela comissoes cada vez que um vendedor zer uma nova venda (tabela vendas) cujo preo seja maior que 400 A comisso dever ser de 15% do valor da venda Tabela comissoes tem colunas: comissao_id, vendedor_id, item_id, comissao
28
Perguntas? robertomello@2reng.com.br
31