CodeGym /Cursos /SQL SELF /Chamada de procedures e funções dentro de transações

Chamada de procedures e funções dentro de transações

SQL SELF
Nível 53 , Lição 1
Disponível

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 SAVEPOINT numa procedure PL/pgSQL (vai dar erro de sintaxe).
  • Procedures só podem ser chamadas por um comando SQL separado CALL ..., não por SELECT nem 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/SAVEPOINT vai dar erro.
IMPORTANTE:

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

  1. Funções — só operações atômicas: tudo ou nada. Se der ruim — tudo é desfeito.
  2. 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.
  3. Rollback parcial — só via EXCEPTION: é o jeito oficial e suportado pra rollback parcial (tipo SAVEPOINT).
  4. 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.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION