Nos bancos de dados modernos, a lógica de negócio geralmente fica do lado do servidor — usando procedures e funções. Quando tu trabalha com PostgreSQL, é importante entender a diferença entre funções e procedures (principalmente depois que procedures chegaram na versão 11+) e como elas interagem com transações.
Aqui embaixo eu vou te mostrar os fatos principais sobre a mecânica das transações, chamadas aninhadas e rollback parcial de mudanças em procedures/funções no PostgreSQL 17, de acordo com a documentação oficial e as limitações atuais.
Conceitos chave: funções vs procedures
Função (CREATE FUNCTION) — sempre roda dentro de uma transação externa; dentro de funções não dá pra usar comandos de transação explícitos (BEGIN, COMMIT, ROLLBACK, SAVEPOINT).
- Qualquer alteração é confirmada ou desfeita só no nível da transação externa.
- Pra fazer um "rollback parcial" dentro de funções, tu pode usar
BEGIN ... EXCEPTION ... END, mas isso não permite dar commit dentro da função.
Procedure (CREATE PROCEDURE) — foi criada pra controlar transações direto no servidor (tipo, fazer commits parciais, rollback de etapas, etc).
- Em procedures (PL/pgSQL) tu pode usar
COMMIT,ROLLBACK,SAVEPOINT,RELEASE SAVEPOINT. - IMPORTANTE: não dá pra usar
ROLLBACK TO SAVEPOINTnuma procedure PL/pgSQL (vai dar erro de sintaxe). - Procedures só podem ser chamadas por um comando SQL separado
CALL ..., não porSELECTnem dentro de outras funções.
Como chamar uma procedure/função de dentro de outra?
Funções chamam outras funções "de boa" só usando o nome:
-- Exemplo: função pra calcular desconto
CREATE OR REPLACE FUNCTION calcular_desconto(order_total NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
IF order_total >= 100 THEN
RETURN order_total * 0.1;
ELSE
RETURN 0;
END IF;
END;
$$ LANGUAGE plpgsql;
-- Função de processamento de pedido chama outra função
CREATE OR REPLACE FUNCTION processar_pedido(order_id INT, order_total NUMERIC)
RETURNS VOID AS $$
DECLARE
desconto NUMERIC;
BEGIN
desconto := calcular_desconto(order_total);
RAISE NOTICE 'Desconto: %', desconto;
INSERT INTO orders_log (order_id, order_total, desconto)
VALUES (order_id, order_total, desconto);
END;
$$ LANGUAGE plpgsql;
Tudo rola dentro de uma transação externa só! Se der erro em qualquer função, tudo é desfeito.
Chamada de procedures e transações aninhadas
Procedures podem ser chamadas dentro de outras procedures usando o comando CALL ... (no PostgreSQL 17 rola fazer stack de chamadas tipo CALL proc1() -> CALL proc2()), mas as regras de transação continuam:
- Comandos de transação (
COMMIT,ROLLBACK,SAVEPOINT,RELEASE SAVEPOINT) só estão disponíveis no nível mais alto das procedures. - Se uma procedure com controle de transação for chamada dentro de uma transação explícita já ativa (tipo, pelo cliente sem autocommit), tentar rodar
COMMIT/SAVEPOINTvai dar erro.
procedures não podem ser rodadas dentro de funções ou blocos anônimos (DO ...). Só por comando separado CALL
Exemplo de procedure com controle de transações
-- Procedure com commit por etapa (só funciona no modo autocommit da conexão)
CREATE PROCEDURE processar_pedidos_em_lote()
LANGUAGE plpgsql
AS $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN SELECT order_id, order_total FROM incoming_orders LOOP
BEGIN
-- Salva cada lote de dados separadamente
INSERT INTO orders (order_id, total) VALUES (rec.order_id, rec.order_total);
EXCEPTION WHEN OTHERS THEN
INSERT INTO order_errors(order_id, err_text) VALUES (rec.order_id, SQLERRM);
END;
COMMIT;
END LOOP;
END;
$$;
-- Chamando a procedure
CALL processar_pedidos_em_lote();
Depois de cada COMMIT começa automaticamente uma nova transação.
Rollback parcial (comportamento tipo savepoint) em PL/pgSQL
PL/pgSQL (tanto em funções quanto em procedures) não suporta o comando ROLLBACK TO SAVEPOINT.
Pra desfazer mudanças de parte do código, só dá pra usar bloco BEGIN ... EXCEPTION ... END:
BEGIN
-- algumas ações
BEGIN
-- operação que pode dar erro
EXCEPTION WHEN OTHERS THEN
-- todas as mudanças desse bloco vão ser desfeitas
RAISE NOTICE 'Rollback dentro do bloco!';
END;
END;
Em procedures também dá pra usar SAVEPOINT e RELEASE SAVEPOINT, mas não ROLLBACK TO SAVEPOINT. O sentido deles é separar etapas, mas tu só controla eles tratando exceções.
Limitações e boas práticas
- Funções — só operações atômicas: tudo ou nada. Se der ruim — tudo é desfeito.
- Procedures — só via CALL: e só por comando SQL separado, não por SELECT/funções. Controle de transação aninhado é possível, mas só seguindo as limitações do PL/pgSQL.
- Rollback parcial — só via EXCEPTION: é o jeito oficial e suportado pra rollback parcial (tipo SAVEPOINT).
- Procedures aninhadas só controlam transações quando chamadas via CALL: senão vai dar erro.
Perguntas sobre lógica e transações
Posso fazer uma "transação aninhada" dentro de uma função?
Não. Tudo roda numa transação só. Pra rollback parcial — só blocos EXCEPTION.
Posso dar COMMIT/ROLLBACK dentro de função ou bloco anônimo?
Não, isso dá erro de sintaxe. Usa procedures pra isso.
Dá pra chamar procedure de dentro de função?
Não, só com comando CALL. De função/SELECT — não rola.
Posso fazer ROLLBACK TO SAVEPOINT numa procedure?
Não! No PL/pgSQL isso é proibido. Usa blocos EXCEPTION.
GO TO FULL VERSION