Então, imagina que você criou uma função ou procedimento cabuloso. Você já tá vendo como seu banco de dados tá rodando lindão, mas aí — pá! — os dados não batem, as queries ficam lentas, e o chefe começa a ficar bolado. É aí que entra a depuração.
Depurar em PL/pgSQL serve pra:
- Achar erros de lógica, tipo quando a função retorna algo diferente do esperado.
- Entender dados de entrada zuados. Porque às vezes o usuário do banco manda não só dados, mas... umas paradas nada a ver!
- Resolver problemas de performance. Afinal, código feito na pressa pode rodar igual tartaruga procurando Wi-Fi no meio do deserto do Saara.
Falando sério, depurar não é só caçar e corrigir erro. É um jeito de deixar seu código melhor, mais rápido, eficiente e fácil de ler.
Principais jeitos de depurar PL/pgSQL
Depurar em PL/pgSQL pode ser feito de várias formas. Bora ver cada uma delas.
- Usando ferramentas nativas do PostgreSQL
O PostgreSQL tem umas paradas nativas pra diagnosticar, tipo funções de log (RAISE NOTICE e RAISE EXCEPTION), e também análise do plano de execução (EXPLAIN ANALYZE). Essas ferramentas ajudam a sacar o que tá rolando dentro da sua função.
- Logando com
RAISE NOTICE
RAISE NOTICE é seu brother se você quer ver quais dados passam pela função, em que ponto deu ruim, ou checar valores das variáveis. Diferente do RAISE EXCEPTION, ele não para a execução da função. Por exemplo, dá pra mostrar o valor da variável em cada etapa.
DO $$
DECLARE
counter INT := 0;
BEGIN
FOR counter IN 1..5 LOOP
RAISE NOTICE 'Valor atual do contador: %', counter;
END LOOP;
END $$;
Esse código mostra os valores do counter de 1 até 5. Simples, mas muito útil pra depurar!
- Usando ferramentas de terceiros
Você também pode depurar PL/pgSQL com ferramentas tipo pgAdmin (com interface gráfica). Dá pra colocar breakpoint e ver o valor das variáveis em tempo real. Se você curte um visual, o pgAdmin vai ser seu parceiro.
Etapas da depuração
Quando a gente começa a depurar uma função ou procedimento, é importante seguir uma ordem. Bora ver cada etapa com mais detalhes:
- Análise dos dados de entrada
A primeira coisa é sacar os dados de entrada. Confere se os dados que sua função recebe não têm erro ou valor estranho. Por exemplo, você pode checar todos os parâmetros de entrada usando RAISE NOTICE:
CREATE FUNCTION check_input(x INTEGER) RETURNS VOID AS $$
BEGIN
IF x IS NULL THEN
RAISE EXCEPTION 'O valor de entrada não pode ser NULL!';
END IF;
RAISE NOTICE 'Valor de entrada: %', x;
END;
$$ LANGUAGE plpgsql;
Esse exemplo mostra como avisar o usuário sobre problema nos dados de entrada.
- Checar cada etapa da execução
Divide sua função em blocos lógicos e coloca RAISE NOTICE nos pontos chave. Assim você descobre onde tá dando ruim.
CREATE FUNCTION calculate_discount(price NUMERIC, discount NUMERIC) RETURNS NUMERIC AS $$
BEGIN
RAISE NOTICE 'Início da função: preço %, desconto %', price, discount;
IF price <= 0 THEN
RAISE EXCEPTION 'O preço não pode ser negativo ou zero!';
END IF;
IF discount < 0 OR discount > 100 THEN
RAISE EXCEPTION 'O desconto deve estar entre 0 e 100!';
END IF;
RETURN price - (price * discount / 100);
END;
$$ LANGUAGE plpgsql;
Aqui, em cada etapa da depuração, aparecem mensagens úteis sobre o que tá rolando.
- Otimizar e resolver problemas
- Depois que você achou o erro, corrige ele. Se o problema for performance, usa ferramentas de análise tipo
EXPLAIN ANALYZEpra otimizar as queries.
Como usar na prática as skills de depuração
Bora ver um caso real: temos uma função que adiciona um registro na tabela e retorna o id gerado. Parece simples, mas às vezes a função dá erro e a gente quer entender o motivo.
Função original:
CREATE FUNCTION add_student(name TEXT, age INTEGER) RETURNS INTEGER AS $$
DECLARE
new_id INTEGER;
BEGIN
INSERT INTO students (name, age) VALUES (name, age) RETURNING id INTO new_id;
RETURN new_id;
END;
$$ LANGUAGE plpgsql;
Se você chamar essa função com dados errados, tipo age < 0, ela vai dar erro. Bora melhorar ela usando logs de depuração.
Função melhorada com log:
CREATE FUNCTION add_student(name TEXT, age INTEGER) RETURNS INTEGER AS $$
DECLARE
new_id INTEGER;
BEGIN
-- Logando os dados de entrada
RAISE NOTICE 'Adicionando estudante: nome %, idade %', name, age;
-- Checando se a idade tá certa
IF age < 0 THEN
RAISE EXCEPTION 'A idade não pode ser negativa!';
END IF;
-- Adiciona o estudante e retorna o ID
INSERT INTO students (name, age) VALUES (name, age) RETURNING id INTO new_id;
-- Logando sucesso
RAISE NOTICE 'Estudante adicionado com ID %', new_id;
RETURN new_id;
END;
$$ LANGUAGE plpgsql;
Agora, se der erro, você vai saber certinho o que rolou, graças às mensagens do RAISE NOTICE.
Dicas finais pra fechar
- Não esquece de tirar logs desnecessários do código em produção.
RAISE NOTICEé ótimo pra depurar, mas se deixar no código final, pode lotar o log do sistema. - Trabalhe com pedaços pequenos de código. Se a função ficou muito grande, divide em partes menores. Fica bem mais fácil de depurar.
- Pratique sempre. Quanto mais você escreve e depura código, mais rápido fica pra achar e corrigir bug.
Depurar é tipo ser detetive, só que ao invés de lupa você usa SQL e lógica de PL/pgSQL. Você vai pegando o jeito com o tempo, e cada bug resolvido te deixa mais ninja!
GO TO FULL VERSION