# Vistas, gatilhos e controlo de acessos

CREATE VIEW, CREATE TRIGGER e GRANT e REVOKE com testes.

Página: https://resumos.rgo.pt/cadeiras/bd/vistas-gatilhos-acessos/

Tabelas guardam factos; o resto da gestão faz-se com objetos que vivem por cima delas. Uma **vista** (_view_) é uma pergunta guardada com nome, um **gatilho** (_trigger_) é código que dispara sozinho em cada escrita, e o **controlo de acessos** decide que utilizadores podem fazer o quê.

## Vistas: perguntas com nome

“Total gasto por cada cliente” da página de consultas merece nome próprio se for pedida todos os dias:

```
CREATE VIEW TotalPorCliente AS
SELECT Cliente.nome AS nome, COALESCE(SUM(Item.qtd * Produto.preco), 0) AS total
FROM Cliente
LEFT JOIN Encomenda ON Encomenda.idCliente = Cliente.id
LEFT JOIN Item ON Item.idEncomenda = Encomenda.id
LEFT JOIN Produto ON Produto.id = Item.idProduto
GROUP BY Cliente.nome;
```

Depois usa-se como tabela: `SELECT * FROM TotalPorCliente WHERE total > 100;` devolve só a Ana com 295. A vista não duplica dados, recalcula em cada uso, por isso reflete sempre as vendas atuais. Serve para simplificar (`SELECT` curto em vez da junção de quatro tabelas) e para segurança (mostrar totais sem expor linhas individuais).

## Gatilhos: regras que se cumprem sozinhas

O `CHECK (stock >= 0)` impede stock negativo em escritas diretas, mas uma venda deve _decrementar_ o stock e falhar se não houver. Um gatilho corre antes de cada atualização e aborta a operação proibida:[1](https://resumos.rgo.pt/cadeiras/bd/vistas-gatilhos-acessos/#user-content-fn-trigger-sintaxe)

```
CREATE TRIGGER impede_stock_negativo
BEFORE UPDATE OF stock ON Produto
FOR EACH ROW
WHEN NEW.stock < 0
BEGIN
  SELECT RAISE(ABORT, 'Stock nao pode ser negativo');
END;
```

Testa o gatilho com uma venda possível e uma impossível. Executa e lê o erro e o stock final:

```sql
CREATE TABLE Produto(id INTEGER PRIMARY KEY, nome TEXT NOT NULL, stock INTEGER NOT NULL CHECK (stock >= 0));
INSERT INTO Produto VALUES (12, 'Monitor', 5);
CREATE TRIGGER impede_stock_negativo BEFORE UPDATE OF stock ON Produto FOR EACH ROW WHEN NEW.stock < 0 BEGIN SELECT RAISE(ABORT, 'Stock nao pode ser negativo'); END;
UPDATE Produto SET stock = stock - 1 WHERE id = 12;
UPDATE Produto SET stock = stock - 10 WHERE id = 12;
SELECT id, nome, stock FROM Produto;
```

A primeira venda passa (5 para 4). A segunda aborta com a mensagem do gatilho e o resultado final mostra `12|Monitor|4`: a instrução proibida foi desfeita sem tocar no stock. Repara na diferença para o `CHECK`: o gatilho reage ao _evento_ (a tentativa de atualização) e pode olhar para os valores antigos (`OLD`) e novos (`NEW`).

Quando dispara cada tipo de gatilho:

Corre antes da escrita e pode abortá-la: é o guarda da porta. O `impede_stock_negativo` acima é `BEFORE`, porque uma venda impossível nem deve começar.

Corre depois da escrita confirmada e serve para efeitos secundários, como auditoria. Este regista cada venda numa tabela de rasto:

```
CREATE TABLE Venda(id INTEGER PRIMARY KEY, total REAL NOT NULL);
CREATE TABLE Auditoria(id INTEGER PRIMARY KEY, texto TEXT NOT NULL);
CREATE TRIGGER regista_venda
AFTER INSERT ON Venda
FOR EACH ROW
BEGIN
  INSERT INTO Auditoria(texto) VALUES ('venda ' || NEW.id);
END;
```

Substitui a escrita e só existe sobre vistas, que não aceitam escritas diretas. Este permite inserir nomes através da vista `Nomes`:

```
CREATE TRIGGER insere_nome
INSTEAD OF INSERT ON Nomes
FOR EACH ROW
BEGIN
  INSERT INTO Produto(nome) VALUES (NEW.nome);
END;
```

## Controlo de acessos

Em SQL padrão, o dono dá e tira permissões por utilizador e por objeto:

```
GRANT SELECT ON TotalPorCliente TO funcionario;
REVOKE INSERT ON Produto FROM funcionario;
```

O funcionário consulta os totais mas não altera produtos. Atenção a um detalhe prático: o SQLite, o software da cadeira, não tem utilizadores nem implementa `GRANT` e `REVOKE`; aqui o controlo de acessos estuda-se como linguagem padrão, a usar num SGBD com contas, como o PostgreSQL. No teste, responde o SQL padrão; no projeto em SQLite, a proteção equivalente faz-se com vistas e gatilhos.

## Para saber mais

*   Sintaxe do `CREATE TRIGGER` com `OLD`, `NEW`, `WHEN` e `RAISE`: [sqlite.org/lang\_createtrigger.html](https://sqlite.org/lang_createtrigger.html).

## Notas de rodapé

1.  A sintaxe com `OLD`, `NEW`, `WHEN` e `RAISE(ABORT, ...)` é a documentada em [sqlite.org/lang\_createtrigger.html](https://sqlite.org/lang_createtrigger.html). [Voltar](https://resumos.rgo.pt/cadeiras/bd/vistas-gatilhos-acessos/#user-content-fnref-trigger-sintaxe)
