CodeGym /Cursos /SQL SELF /Análise dos erros mais comuns ao trabalhar com transações...

Análise dos erros mais comuns ao trabalhar com transações aninhadas

SQL SELF
Nível 54 , Lição 4
Disponível

Programar em Postgres é tipo uma aventura: às vezes parece um jogo chamado “Ache seu erro”. Aqui a gente vai falar dos erros clássicos e das armadilhas que aparecem quando você mexe com transações aninhadas. Bora lá!

Uso errado de comandos transacionais dentro de funções e procedimentos

Erro: tentar usar COMMIT, ROLLBACK ou SAVEPOINT dentro de uma FUNCTION.

Por quê: No PostgreSQL, funções (CREATE FUNCTION ... LANGUAGE plpgsql) sempre rodam dentro de uma transação externa, e qualquer comando transacional dentro da função é proibido. Se tentar, vai dar erro de sintaxe.

Exemplo de erro:

CREATE OR REPLACE FUNCTION f_bad() RETURNS void AS $$
BEGIN
    SAVEPOINT sp1;  -- Erro: comandos transacionais são proibidos
END;
$$ LANGUAGE plpgsql;

Como fazer certo:

Para operações atômicas, que precisam ser “tudo ou nada”, use funções sem comandos transacionais explícitos. Se precisar salvar mudanças em etapas — use procedimentos.

Erro: tentar usar ROLLBACK TO SAVEPOINT em um procedimento em PL/pgSQL.

Por quê: No PostgreSQL 17 só são permitidos os comandos COMMIT, ROLLBACK, SAVEPOINT, RELEASE SAVEPOINT dentro de procedimentos (CREATE PROCEDURE ... LANGUAGE plpgsql). Mas ROLLBACK TO SAVEPOINT em PL/pgSQL não pode! Qualquer tentativa vai dar erro de sintaxe.

Exemplo de erro:

CREATE PROCEDURE p_bad()
LANGUAGE plpgsql
AS $$
BEGIN
    SAVEPOINT sp1;
    -- ...
    ROLLBACK TO SAVEPOINT sp1; -- Erro! Não pode usar
END;
$$;

Como fazer certo:

Para “rollback parcial” usa blocos BEGIN ... EXCEPTION ... END — eles criam um savepoint automático; se rolar erro dentro do bloco, tudo volta pro começo dele.

CREATE PROCEDURE p_good()
LANGUAGE plpgsql
AS $$
BEGIN
    BEGIN
        -- operações que podem dar erro
        ...
    EXCEPTION
        WHEN OTHERS THEN
            RAISE NOTICE 'Rollback dentro do bloco BEGIN ... EXCEPTION ... END';
    END;
END;
$$;

Chamadas aninhadas de procedimentos: limitações e erros comuns

Erro: chamar um procedimento com COMMIT/ROLLBACK explícito dentro de uma transação já aberta pelo cliente.

Por quê: procedimentos com controle transacional só funcionam direito no modo autocommit (um procedimento — uma transação), senão, se tentar usar COMMIT ou ROLLBACK dentro do procedimento, vai dar erro: a transação já tá aberta no cliente.

Exemplo:

# No Python com psycopg2, por padrão autocommit=False
cur.execute("BEGIN;")
cur.execute("CALL my_proc();")   -- Erro ao tentar COMMIT dentro do my_proc

Como fazer certo:

  • Antes de chamar procedimentos, coloca a conexão em modo autocommit.
  • Não chama procedimentos por funções ou SELECT.

Erro: chamar procedimentos com controle transacional (COMMIT, ROLLBACK) não funciona se não for pelo comando CALL (tipo, via SELECT).

Por quê: Só chamada via CALL (ou num bloco DO anônimo) permite controlar transações. Chamar de função — não pode.

Problemas com locks e deadlocks

Locks são tipo visita indesejada: primeiro incomodam, depois viram bagunça. Deadlock rola quando as transações ficam esperando uma pela outra pra sempre. Olha um exemplo clássico:

  1. Transação A trava uma linha na tabela orders e tenta atualizar uma linha na tabela products.
  2. Transação B trava uma linha na tabela products e tenta atualizar uma linha na tabela orders.

No fim, nenhuma das transações consegue continuar. É tipo dois carros tentando entrar na mesma curva ao mesmo tempo — vira engarrafamento.

Exemplo:

-- Transação A
BEGIN;
UPDATE orders SET status = 'Processando' WHERE id = 1;

-- Transação B
BEGIN;
UPDATE products SET stock = stock - 1 WHERE id = 10;

-- Agora a transação A tenta atualizar a mesma linha em `products`,
-- e a transação B tenta mudar a linha em `orders`.
-- Deadlock!

Como evitar?

  1. Sempre atualize os dados na mesma ordem. Tipo, primeiro orders, depois products.
  2. Evite transações demoradas demais.
  3. Use LOCK com cuidado, escolhendo o menor nível de lock possível.

Uso errado de SQL dinâmico (EXECUTE)

SQL dinâmico, se usar sem cuidado, pode virar dor de cabeça. O erro mais comum é SQL injection. Tipo assim:

EXECUTE 'SELECT * FROM orders WHERE id = ' || user_input;

Se user_input for algo tipo 1; DROP TABLE orders;, já era a tabela orders.

Como evitar? Use queries preparadas:

EXECUTE 'SELECT * FROM orders WHERE id = $1' USING user_input;

Assim seu app fica protegido de SQL injection.

Rollback da transação depois de tratar erro errado

Se os erros não forem tratados direito, a transação pode ficar num estado inválido. Tipo assim:

BEGIN;

INSERT INTO orders (order_id, status) VALUES (1, 'Pendente');

BEGIN;
-- Alguma operação que dá erro
INSERT INTO non_existing_table VALUES (1);
-- Erro, mas a transação não foi finalizada

COMMIT; -- Erro: transação atual foi abortada

Por causa do erro, o código trava tudo.

Como evitar? Use blocos EXCEPTION pra fazer rollback direito:

BEGIN
    INSERT INTO orders (order_id, status) VALUES (1, 'Pendente');
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE NOTICE 'Rolou um erro, a transação vai ser desfeita.';
END;

Como evitar erros: dicas e recomendações

  • Quando for escrever um procedimento complicado, começa com um pseudocódigo. Escreve todos os passos e onde pode dar ruim.
  • Use SAVEPOINT pra rollback isolado. Mas não esquece de liberar depois de usar.
  • Evite transações longas — quanto mais tempo, mais chance de lock.
  • Pra chamadas aninhadas de procedimentos, garante que o contexto de transação externo e interno estão sincronizados.
  • Sempre testa a performance dos seus procedimentos com EXPLAIN ANALYZE.
  • Loga os erros em tabelas ou arquivos de texto — isso ajuda muito na hora de debugar.

Exemplos de erros e como corrigir

Exemplo 1: Erro ao chamar procedimento aninhado

Código com erro:

BEGIN;

CALL process_order(5);

-- Dentro do process_order rolou um ROLLBACK
-- A transação inteira fica inválida
COMMIT; -- Erro

Código corrigido:

BEGIN;

SAVEPOINT sp_outer;

CALL process_order(5);

-- Rollback só se der erro
ROLLBACK TO SAVEPOINT sp_outer;

COMMIT;

Exemplo 2: Problema de Deadlock

Código com erro:

-- Transação A
BEGIN;
UPDATE orders SET status = 'Processando' WHERE id = 1;
-- Espera `products`

-- Transação B
BEGIN;
UPDATE products SET stock = stock - 1 WHERE id = 10;
-- Espera `orders`

Correção:

-- As duas queries rodam na mesma ordem:
-- Primeiro `products`, depois `orders`.
BEGIN;
UPDATE products SET stock = stock - 1 WHERE id = 10;
UPDATE orders SET status = 'Processando' WHERE id = 1;
COMMIT;

Esses erros mostram porque trabalhar com transações exige atenção e experiência. Mas, como dizem, quanto mais prática, menos chance de tomar um ROLLBACK na vida real (e na carreira).

2
Tarefa
SQL SELF, nível 54, lição 4
Bloqueado
Procedimento aninhado usando `EXCEPTION`
Procedimento aninhado usando `EXCEPTION`
1
Pesquisa/teste
Procedures Aninhadas, nível 54, lição 4
Indisponível
Procedures Aninhadas
Procedures Aninhadas
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION