Como encontrar regras ocultas ao migrar PL/SQL
Uma migração de PL/SQL dá certo ao inventariar packages, triggers, estado de sessão e efeitos, e provar paridade antes de mover cada regra.

A parte perigosa de uma migração de PL/SQL não é converter a sintaxe. É descobrir de quais comportamentos do banco a empresa depende antes que o banco antigo deixe de fornecê-los. Corpos de packages, triggers de linha e de instrução, chamadas agendadas, estado da sessão e caminhos de erro podem conter regras que não aparecem em nenhum repositório da aplicação.
Uma migração confiável trata o schema Oracle como um sistema executável, não como um depósito de stored procedures. O inventário estabelece o que existe. O rastreamento estabelece o que roda. Os testes de caracterização estabelecem o significado. Só então a equipe pode decidir se uma regra deve ficar em um serviço Go, um núcleo Rust, um cliente TypeScript, uma restrição do Postgres ou em lugar nenhum.
Inventarie o schema como um sistema executável
Comece com uma extração reproduzível de um banco semelhante ao de produção, pois um checkout do código raramente corresponde ao que o Oracle executa. Correções urgentes são compiladas de uma estação de trabalho, objetos com edições ficam escondidos atrás de sinônimos e permissões determinam quais tabelas o código com direitos do definidor pode acessar. Exporte DDL, fonte, status, metadados de triggers, jobs, sinônimos, permissões e dependências em um snapshot com data e hora.
A view ALL_SOURCE do Oracle expõe o texto fonte de procedures, functions, packages, corpos de packages, triggers, tipos e corpos de tipos acessíveis. Use DBA_SOURCE quando a conta de avaliação tiver o privilégio necessário e o escopo abranger vários schemas. A consulta a seguir produz um inventário estável do código, em vez de um diretório cheio de arquivos anônimos:
SELECT owner,
type,
name,
COUNT(*) AS source_lines
FROM dba_source
WHERE owner IN ('BILLING', 'ORDERS', 'FINANCE')
GROUP BY owner, type, name
ORDER BY owner, type, name;
O resultado tem uma linha por objeto armazenado: OWNER, TYPE, NAME e SOURCE_LINES. Um package aparece em linhas separadas como PACKAGE e PACKAGE BODY. Não as una. A especificação é um contrato público; o corpo contém rotinas privadas, código de inicialização e a maior parte dos detalhes de implementação.
Extraia o DDL de criação além do texto. DBMS_METADATA.GET_DDL preserva detalhes do objeto que a concatenação de ALL_SOURCE.TEXT não preserva. Para um package, solicite PACKAGE_SPEC e PACKAGE_BODY; para um trigger, solicite TRIGGER. Registre junto de cada arquivo a versão do banco, o schema, o status do objeto, a edição e a hora da extração. A equipe deve conseguir responder qual definição compilada produziu um resultado capturado.
Inclua objetos inválidos e erros de compilação em vez de filtrá-los. Um trigger inválido ainda pode bloquear a instrução que o aciona, e um package invalidado por uma alteração de tabela pode recompilar sob demanda em condições que o repositório nunca reproduziu. DBA_OBJECTS.STATUS e DBA_ERRORS transformam essas condições em evidência. Inventarie também views materializadas, colunas virtuais, índices baseados em funções, políticas de acesso refinado e expressões padrão que chamam funções. Nem todos são contêineres PL/SQL, mas cada um pode invocar lógica armazenada ou depender dela.
Faça um segundo snapshot após o período de observação. A diferença costuma revelar ferramentas de implantação, jobs noturnos ou administradores que substituem corpos de packages fora do processo oficial de release. Não comece uma reescrita contra um alvo móvel sem congelar essas mudanças ou incluí-las na extração e nos testes.
A contagem não é a medida de risco. Um trigger de seis linhas que altera silenciosamente um período contábil pode importar mais que um package de relatórios com 9.000 linhas. O inventário define o espaço de busca e permite detectar mudanças; ele não informa qual código carrega significado de negócio.
Encontre pontos de entrada antes de ler os packages
Rastreie quem pode invocar PL/SQL e quais eventos do banco o invocam, depois leia para dentro a partir desses pontos. Ler packages em ordem alfabética desperdiça tempo porque utilitários privados e código morto parecem tão importantes quanto os caminhos usados em todos os pedidos.
Monte uma tabela de pontos de entrada com pelo menos estas fontes:
- Chamadas da aplicação com blocos anônimos,
CALLou nomes qualificados de procedures de package - Triggers DML, DDL, de login, inicialização e instead-of habilitados
- Jobs do agendador e entradas de filas de jobs antigas
- Views e funções chamadas a partir de instruções SQL
- Ferramentas externas, relatórios, carregadores de arquivos e scripts operacionais
Para triggers, capture mais que o corpo. Consulte DBA_TRIGGERS para obter TRIGGERING_EVENT, TRIGGER_TYPE, TABLE_OWNER, TABLE_NAME, STATUS, WHEN_CLAUSE e ACTION_TYPE. Relacione esse inventário às tabelas escritas por cada fluxo da aplicação. Uma regra ligada a ORDERS pode rodar quando um script de suporte atualiza a tabela, mesmo que o serviço principal nunca chame o trigger pelo nome.
A orientação do Oracle sobre triggers PL/SQL traz dois pontos que afetam o desenho da migração. Triggers rodam automaticamente nos eventos definidos, independentemente do usuário ou aplicação que emita a instrução. O código também não deve depender da ordem em que uma instrução SQL processa as linhas. Se o sistema atual viola o segundo ponto por meio de uma variável global de package atualizada por um trigger de linha, o comportamento já pode ser não determinístico. Preserve os resultados observados para a paridade, mas marque a dependência para um redesenho explícito em vez de aceitá-la como requisito.
A inicialização do package é outro ponto de entrada frequentemente ignorado. O Oracle executa a seção de inicialização do corpo na primeira vez em que uma sessão referencia o package. Se ela carrega configuração, calcula uma data de negócio ou define uma variável, a primeira chamada pública tem um prólogo invisível. Procure a região final BEGIN ... END de cada corpo e registre leituras, escritas, exceções e dependências de contexto.
Conclua essa etapa com um grafo de chamadas cujas raízes sejam eventos operacionais, não apenas nomes de objetos. Cada raiz deve identificar o agente iniciador, a transação, o formato da entrada, o package ou trigger alcançado, as tabelas lidas e escritas, os efeitos externos e os erros observados. Campos desconhecidos são úteis: mostram exatamente onde ainda falta evidência em tempo de execução.
Procedures de package com sobrecarga precisam de assinaturas, não apenas nomes. Colete posição, modo e tipo do argumento, presença de valor padrão e identificador de sobrecarga em ALL_ARGUMENTS, depois relacione essas assinaturas aos chamadores. Drivers podem vincular por posição, expor tipos de coleção do Oracle ou depender do formato de um cursor OUT. Trocar ORDER_API.SUBMIT por um endpoint HTTP muda um contrato de comunicação mesmo quando o resultado de negócio é idêntico. Catalogue esse trabalho de compatibilidade separadamente da recuperação das regras.
Dependências estáticas não revelam o programa inteiro
Trate ALL_DEPENDENCIES como um limite inferior útil, pois o Oracle não consegue registrar uma dependência normal de compilação para um nome de objeto montado dentro de SQL dinâmico. Sinônimos, links de banco, resolução com direitos do invocador, contextos de aplicação e strings armazenadas em tabelas aumentam a lacuna.
Comece pelo grafo estático:
SELECT owner,
name,
type,
referenced_owner,
referenced_name,
referenced_type
FROM dba_dependencies
WHERE owner IN ('BILLING', 'ORDERS', 'FINANCE')
ORDER BY owner, name, referenced_owner, referenced_name;
Depois procure no código comportamentos que o grafo não resolve com confiança: EXECUTE IMMEDIATE, DBMS_SQL, OPEN ... FOR, marcadores de links de banco, SYS_CONTEXT, pragmas de transação autônoma, packages de arquivos e filas, chamadas de e-mail e tratadores de exceções que escrevem. Pesquise nos dados nomes configurados de procedures quando a aplicação distribui chamadas com base em metadados.
SQL dinâmico exige uma revisão da origem das strings. Para cada instrução, identifique o modelo, todo identificador substituído, os valores vinculados, o schema usado para resolver nomes e exemplos capturados em execução. Uma linha como EXECUTE IMMEDIATE l_sql USING p_id diz pouco até você saber se l_sql atualiza uma partição conhecida ou chama um package específico do locatário escolhido em uma tabela de configuração.
O comportamento das permissões faz parte da mesma análise. Um package sem AUTHID CURRENT_USER explícito usa por padrão os direitos do definidor, portanto suas referências não qualificadas e permissões não se comportam como uma consulta de serviço emitida com a identidade do usuário final. Registre AUTHID, permissões diretas, papéis, sinônimos e leituras do contexto da aplicação. Mover a rotina para um serviço pode remover sem querer um limite legítimo de autoridade ou dar à conta do serviço muito mais acesso do que o package tinha.
Não responda instrumentando cada linha. Adicione observação nos limites: entrada e saída do package, resultado da transação, disparo do trigger, formato das instruções dinâmicas e chamadas externas. Use um identificador de correlação que sobreviva da solicitação da aplicação até os metadados da sessão. Capture valores vinculados apenas quando a política permitir e remova dados sensíveis antes do armazenamento. O objetivo é um mapa de comportamento, não um segundo banco de produção cheio de segredos.
A análise estática também exagera algumas dependências. O corpo de um package pode conter rotinas abandonadas que fazem referência a tabelas sem uso há anos. Marcar toda aresta como ativa faz o escopo crescer mais rápido que as evidências. Mantenha grafos separados para “pode chamar” e “chamou”, e guarde junto do segundo a janela de observação e a carga. A ausência em um trace não prova que o código está morto, mas permite exigir um responsável ou um teste planejado antes de reconstruir o caminho.
Transforme descobertas em um registro de comportamento
Um registro de comportamento transforma achados no código em contratos testáveis. Cada linha descreve uma regra com significado externo, incluindo entradas, saídas, alterações de estado, falhas e evidências. Sem esse artefato intermediário, arquitetos tendem a atribuir packages inteiros a componentes de destino e levam fronteiras acidentais para a substituição.
Considere uma procedure de package que confirma uma fatura. Ela valida o status, calcula o imposto a partir de tabelas de clientes e datas de vigência, insere lançamentos contábeis, altera o estado da fatura e enfileira uma notificação. Isso não é uma regra só. O registro deve separar elegibilidade, seleção fiscal, contabilização, transição de estado e intenção de notificação porque cada item pode merecer um destino e um oráculo de teste diferentes.
Uma entrada útil no registro contém:
- Identificador da regra e uma declaração em linguagem clara com o local no código
- Evento disparador e estados necessários da sessão, das tabelas e do package
- Entradas, saídas, escritas, mensagens, arquivos, commits e rollbacks
- Casos-limite, erros Oracle, códigos personalizados e comportamento de repetição
- Evidências do código, traces, exemplos de produção e um responsável que aprove
Dê a cada regra um status como observada, inferida, contestada, aprovada ou obsoleta. O código sozinho sustenta “inferida” quando o ramo pode ser inalcançável. Uma execução capturada sustenta “observada”. Finanças ou operações podem aprovar se um comportamento estranho é contratual. Isso evita a reunião comum em que um resultado surpreendente vira bug apenas porque o novo desenho não o reproduziu.
Separe regras de negócio da mecânica do banco. “Uma fatura não pode ser contabilizada em um período fechado” é uma regra. “Um trigger BEFORE INSERT consulta PERIOD_CONTROL e lança -20041” é uma implementação. “Definir UPDATED_AT com SYSTIMESTAMP” pode ser uma política de persistência. “Incrementar uma global do package” pode ser uma solução improvisada. A consequência importa: migre a regra, teste o mecanismo antigo e só o mantenha quando sua semântica fizer parte do contrato.
Registre também o espaço negativo. Se atualizações SQL diretas ignoram uma validação da aplicação, mas ainda atingem um trigger, esse trigger define a fronteira real de imposição. Se um package faz commit internamente, os chamadores não podem reverter o fluxo inteiro mesmo que o código pareça controlar a transação. Esses fatos incômodos determinam a virada e não devem ser suavizados em textos de arquitetura.
Torne divergências executáveis quando possível. Se operações diz que um cancelamento retroativo é permitido e finanças diz que não, preserve os dois exemplos candidatos, os resultados esperados e as condições de dados. Peça ao responsável para aprovar um resultado no registro. Um requisito em prosa como “tratar retroatividade corretamente” passa por toda revisão sem dar à equipe algo que possa comparar.
O registro também controla exclusões. Quando ninguém consegue fornecer um agente invocador, uma execução observada, um motivo regulatório ou um exemplo aprovado para uma rotina, marque-a como candidata à remoção. Preserve o código antigo e prove que os chamadores não a alcançam; não gaste tempo de migração traduzindo-a só porque compila.
Teste transações, não funções isoladas
Testes de caracterização devem acionar o sistema antigo pelos limites públicos reais e comparar o resultado completo da transação. Um teste unitário de uma função fiscal privada ignora efeitos de triggers, estado de packages, configurações NLS, consumo de sequences, tradução de exceções e comportamento de commit.
Crie um ambiente Oracle descartável com um conjunto de dados mascarado e consistente nas referências. Fixe explicitamente as entradas da sessão: fuso horário, NLS_DATE_FORMAT, caracteres numéricos, schema atual, contexto da aplicação e fontes da data de negócio. Restaure tabelas e sequences a uma base conhecida em cada caso quando identificadores exatos importarem. Use sessões separadas em testes com estado de package.
O Oracle documenta que cada sessão recebe sua própria instância do package e que os valores de um package com estado normalmente persistem durante a sessão. Recompilar um package com estado já instanciado pode descartar esse estado e causar ORA-04068 na próxima chamada. Um pool de conexões transforma globais de package em memória oculta por conexão. A suíte precisa de casos para uma sessão nova, chamadas repetidas na mesma sessão, duas sessões simultâneas, reutilização de uma sessão do pool, rollback e invalidação do package se implantações de produção puderem causá-la.
Para cada teste, capture um registro de observação normalizado:
{
"case": "closed-period-credit",
"entry": "billing.invoice_api.post_credit",
"result": {"status": "error", "oracle_code": -20041},
"tables": {"invoice": [], "ledger_entry": []},
"events": [],
"transaction": "rolled_back"
}
A nova implementação deve produzir a mesma observação de negócio, não necessariamente a mesma pilha do Oracle ou valor de sequence. Normalize identificadores gerados em aliases estáveis, compare dinheiro na escala declarada, ordene conjuntos apenas quando a ordem for contratual e compare timestamps com a precisão exposta pela interface antiga. Preserve códigos de erro personalizados exatos quando chamadores decidirem com base neles; caso contrário, mapeie-os para um erro de domínio explícito na fronteira de compatibilidade e teste esse mapeamento.
Teste falhas e trabalho parcial de propósito. Force uma chave duplicada depois de inserir uma auditoria. Deixe a fila de notificações indisponível. Lance uma exceção em um trigger de linha após processar várias linhas. Exercite DML em lote, em que o momento dos triggers de instrução e de linha altera o que permanece. Um resultado de sucesso não revela uma transação autônoma que gravou uma linha de auditoria mesmo com a transação de negócio revertida.
Uma falha comum acontece assim: a aplicação inicia uma transação, chama um package para reservar crédito, insere um pedido e reverte quando a alocação do estoque falha. O package atualiza um cache global de “crédito disponível” e uma rotina autônoma de auditoria faz commit de um registro de reserva. A atualização da tabela é revertida, mas o valor do package permanece naquela sessão do pool e o registro de auditoria continua confirmado. Uma substituição que coloca tudo em uma transação limpa do serviço terá outro comportamento na solicitação seguinte. O registro deve decidir se esses resíduos são necessários, defeitos tolerados ou comportamento a remover, e a suíte deve fixar a escolha aprovada.
A ordem dos triggers precisa de casos próprios quando vários deles compartilham tabela e ponto temporal. O Oracle oferece FOLLOWS e, em casos limitados, PRECEDES para relações declaradas, mas triggers sem relação não ganham uma ordem total confiável. Capture o DDL e teste os resultados finais com instruções de várias linhas. Não escreva um teste do destino que exija uma sequência acidental, a menos que a fonte a declare e o resultado de negócio dependa dela.
Meça a cobertura por regras do registro e pontos de entrada, não por linhas PL/SQL. Um teste pode executar todas as linhas de um package fiscal sem provar qual taxa vence em um limite de vigência. Em contraste, uma matriz compacta de datas, jurisdições, classes de clientes e estados de reversão pode caracterizar o contrato enquanto deixa ramos defensivos sem visita. Mantenha a cobertura comum de código como diagnóstico, não como critério de aceitação.
O tráfego de produção fornece casos, não a verdade
O tráfego de produção gravado é a fonte mais forte de entradas realistas, mas não define toda a especificação. Ele traz casos normais em excesso, contém acidentes históricos e raramente captura as entradas contrárias que deveriam falhar.
Registre solicitações na fronteira em que o significado da entrada ainda é visível. Inclua a operação chamada, as chamadas ordenadas dentro de uma transação, parâmetros higienizados, contexto relevante da sessão, classe do resultado e identificadores necessários para coletar as linhas afetadas. Para pontos de entrada conduzidos por SQL, registre valores vinculados e agrupamento de transações em vez de apenas o texto SQL. Em trabalhos em lote, preserve o formato do arquivo e totais de controle sem copiar dados restritos para um armazenamento de testes sem controle.
Reproduza cada caso contra as implementações antiga e nova a partir de estados iniciais equivalentes. Compare valores retornados, erros, alterações no banco, eventos emitidos e fronteiras de commit. Quando os resultados divergirem, classifique o motivo antes de alterar o código: regra ausente, redesenho intencional, não determinismo, fixture ruim ou defeito antigo que o responsável decidiu eliminar.
O tráfego deve ser complementado com casos planejados a partir do registro. Adicione datas-limite, nulos, solicitações duplicadas, novas tentativas após timeout, escala numérica máxima, usuários não autorizados, períodos contábeis fechados e atualizações simultâneas da mesma entidade. Acrescente verificações metamórficas quando for difícil enumerar um resultado exato. Por exemplo, contabilizar e depois reverter uma fatura elegível deve deixar seu efeito líquido no livro em zero, respeitada a regra antiga de arredondamento.
Faça amostragem por comportamento além de volume. Um milhão de pedidos bem-sucedidos acrescenta pouca evidência quando os formatos de entrada se repetem, enquanto um fechamento anual, uma mudança de horário ou um ajuste manual pode cobrir um ramo único. Preserve deliberadamente casos raros e dê a eles dados estáveis. A frequência de produção deve orientar testes de desempenho; a consequência de negócio e a singularidade do ramo devem orientar a cobertura de paridade.
CodeHero usa um sistema de paridade com tráfego de produção gravado ao reescrever sistemas, inclusive bases PL/SQL, e lê as linguagens ao redor do código de banco como uma só base de código. Isso importa porque o contrato de uma stored procedure costuma estar parcialmente em um chamador Java, um script agendado e nas linhas que um trigger altera. O sistema ainda precisa dos casos negativos e de limite do registro; o volume reproduzido não prova um comportamento que o tráfego nunca exercitou.
Não envie entradas capturadas de produção a um modelo ou ambiente de testes sem uma decisão de classificação dos dados. O mascaramento deve preservar propriedades usadas pelas regras, como grupos de igualdade, ordem de datas, prefixos de conta e integridade referencial. Trocar todos os valores por texto aleatório pode proteger a identidade enquanto destrói exatamente os casos que a migração precisa testar.
Coloque cada regra em sua fronteira confiável mais estreita
Coloque uma regra onde toda escrita relevante precise passar e onde a equipe possa observá-la e testá-la. Isso normalmente produz uma arquitetura dividida, não uma campanha para mover todo o PL/SQL para serviços ou manter todas as regras no Postgres.
Use restrições do banco para invariantes expressáveis contra a linha ou o estado relacional: possibilidade de nulo, unicidade, chaves estrangeiras e faixas verificáveis. Restrições abrangem todos os escritores e fornecem fatos úteis ao planejador de consultas. Não substitua uma restrição declarativa por código da aplicação só porque o código parece mais fácil de versionar.
Mantenha uma pequena função de banco ou trigger apenas quando a regra realmente se aplicar a todos os escritores, não puder ser expressa de forma declarativa e esses escritores continuarem contornando um único serviço. Deixe os efeitos explícitos e mínimos. Um trigger que grava contexto de auditoria pode ser defensável; um trigger que calcula preços, escreve cinco tabelas, envia uma mensagem e faz commit autônomo esconde um fluxo que precisa de uma API com responsável.
Coloque regras de fluxo em um serviço quando coordenarem agregados, chamarem sistemas externos, exigirem novas tentativas explícitas ou precisarem de observação no nível do produto. O serviço deve controlar a transação ou usar uma outbox para o trabalho posterior ao commit. Não publique uma mensagem antes do commit esperando que consumidores tolerem um rollback. Não deixe o trigger de compatibilidade e o novo serviço enviarem a mesma mensagem.
Coloque núcleos numéricos em Rust somente quando o trabalho se beneficiar de uma fronteira de cálculo estreita e testável de forma independente. Coloque regras de apresentação no cliente TypeScript apenas quando o servidor ou banco ainda impuser o invariante subjacente. Um botão desativado oferece informação útil; não é autorização.
Postgres não é Oracle com outra grafia. O estado de sessão de packages não tem um destino direto, strings vazias e nulos diferem, exceções e transações autônomas se comportam de outra forma, e a ordem de triggers merece um desenho explícito. Modernize essas fronteiras em vez de transliterá-las. Uma solicitação de serviço, uma transação e um registro de outbox explícitos são mais fáceis de entender que uma nova teia de callbacks ocultos.
Um registro de decisão para cada regra deve nomear o responsável escolhido, o ponto de imposição, o plano de compatibilidade, os casos de teste e a condição para remover a implementação Oracle. Se ninguém é responsável por uma regra, ela não foi movida. Se dois componentes a aplicam, documente qual tem autoridade e por quanto tempo a duplicação vai durar.
Faça a virada sem executar regras duas vezes
O maior risco na virada é o comportamento duplicado: o novo serviço executa uma regra enquanto um trigger antigo a executa novamente sem aviso. Isso cria lançamentos contábeis duplicados, notificações repetidas, timestamps conflitantes ou uma atualização que passa em um validador e falha no outro.
Monte uma matriz de ativação regra por regra. As linhas são identificadores do registro. As colunas são aplicação antiga, package Oracle, trigger Oracle, serviço novo, restrição ou trigger Postgres e consumidor de eventos. Para cada estado de implantação, marque um executor com autoridade e eventuais observadores. Recuse um estado com dois executores, a menos que a operação seja comprovadamente idempotente e a duplicação seja intencional.
A execução em sombra não deve alterar o estado compartilhado de produção. Rode a nova lógica de decisão em modo de observação, ou reproduza entradas capturadas contra um destino isolado, e compare o resultado proposto com o resultado confirmado pelo Oracle. Para fluxos com efeitos externos, substitua o destino por um coletor que registre a intenção sem enviar e-mail, cobrar uma conta ou publicar em um tópico ativo.
Escritas duplas são populares porque parecem facilitar o rollback. Em geral, criam dois modos de falha e uma fonte de verdade ambígua. Prefira um escritor com captura de mudanças ou outbox, com atraso de replicação medido e uma consulta de conciliação. Se escritas duplas temporárias forem inevitáveis, atribua uma chave de idempotência na fronteira da solicitação original e persista o resultado nos dois lados.
Faça a virada por ponto de entrada coerente, não por um arquivo arbitrário de package. Mova a procedure, os triggers de que ela depende, sua semântica transacional e seus efeitos posteriores como uma unidade de comportamento. Bloqueie ou redirecione escritores diretos que contornariam a nova autoridade. Mantenha uma fachada de compatibilidade apenas quando os chamadores precisarem de tempo, e faça com que ela chame o novo responsável em vez de conter uma segunda implementação.
O rollback deve especificar a direção dos dados, não apenas a direção da implantação. Diga qual sistema mantém a autoridade, quais escritas pausam, como registros exclusivos do destino voltam ao Oracle se necessário e como os efeitos emitidos são conciliados. Reverter um contêiner não reverte o negócio depois que dinheiro, mensagens ou arquivos saíram da transação.
Desligar o Oracle é um teste de aceitação
Uma migração de PL/SQL termina quando a empresa pode operar com o comportamento relevante do Oracle desativado e a equipe consegue provar por que os resultados continuam corretos. “Todos os corpos de packages traduzidos” não diz nada sobre triggers, jobs, variáveis globais de sessão, scripts operacionais ou chamadores que ainda se conectam diretamente.
Para cada ponto de entrada, exija uma cadeia fechada do agente iniciador até uma regra aprovada, um responsável no destino, casos de caracterização aprovados, estado da virada e observação em produção. Repita o inventário do schema e compare com o snapshot inicial. Todo trigger ainda habilitado, permissão de execução, job agendado, sinônimo e conexão de aplicação precisa de um motivo explícito.
Depois faça um teste de negação em um ambiente semelhante ao de produção. Revogue o caminho antigo ou desative o trigger migrado, rode toda a suíte de tráfego e casos planejados e monitore tentativas de conexão. O teste deve falhar se algum caminho ainda depender do Oracle. Uma execução bem-sucedida fornece evidência mais forte que uma planilha em que cada responsável marcou sua linha como concluída.
Mantenha o snapshot do código, o registro de comportamento, as observações normalizadas, as decisões e os resultados de paridade como um único conjunto de evidências. Eles explicam mais que o funcionamento do código antigo. Mostram quais esquisitices a empresa aceitou, quais defeitos eliminou e onde cada regra sobrevivente passou a viver.
CodeHero entrega reescritas de sistemas legados em menos de 30 dias, mas velocidade não justifica adivinhar o comportamento do banco. Para avançar rápido, inspecione o sistema inteiro de uma vez, transforme descobertas em comparações executáveis e não considere uma regra migrada enquanto o Oracle ainda precisar impô-la.
Perguntas frequentes
Como encontro todo o código PL/SQL em um banco Oracle?
Consulte DBA_SOURCE ou ALL_SOURCE para packages, corpos, procedures, functions, triggers e tipos, depois extraia o DDL com DBMS_METADATA. Inclua jobs, permissões, sinônimos, objetos inválidos, colunas virtuais, índices baseados em funções e políticas, pois o código sozinho não descreve todo caminho de chamada.
ALL_DEPENDENCIES encontra todas as dependências PL/SQL?
Não. Ela registra dependências normais de compilação, mas SQL dinâmico, nomes configurados, sinônimos, links de banco, resolução com direitos do invocador e chamadas externas podem escapar do grafo. Combine-a com buscas no código e traces de execução.
A lógica de negócio deve sair dos triggers?
Mova fluxos e efeitos externos para um serviço com responsável, mas mantenha invariantes universais na fronteira mais estreita que todo escritor precise cruzar. Uma restrição declarativa costuma ser melhor que um trigger ou verificações duplicadas na aplicação.
Como testo um package PL/SQL antes de reescrevê-lo?
Chame os pontos de entrada públicos contra dados Oracle controlados e capture retornos, erros, alterações de tabelas, eventos e resultados de transação. Repita os casos em sessões novas, reutilizadas, simultâneas e invalidadas se o package mantiver estado.
Por que o estado de packages Oracle importa em uma migração?
Variáveis de package podem persistir durante a sessão, então um pool transporta estado oculto entre solicitações. Uma substituição sem estado pode mudar o comportamento se os testes não expuserem a dependência e os responsáveis não decidirem se ela deve permanecer.
O tráfego de produção basta para provar paridade de PL/SQL?
Não. A reprodução oferece entradas realistas, mas deixa de fora falhas raras, datas-limite, chamadas não autorizadas e ramos nunca usados. Adicione casos planejados a partir de um registro de comportamento e compare os efeitos completos da transação.
Como migrar transações autônomas?
Primeiro identifique por que o código antigo faz commit independente e se a empresa depende de sua sobrevivência após um rollback. A maioria dos casos de auditoria ou mensagens fica mais clara como uma outbox explícita ou uma escrita com responsável próprio, mas o comportamento aprovado deve orientar o desenho.
É possível traduzir Oracle PL/SQL diretamente para PostgreSQL?
A sintaxe pode ser convertida, mas a tradução direta ignora diferenças em estado de package, strings vazias e nulos, erros, privilégios, transações e triggers. Recupere primeiro o comportamento e depois escolha um responsável nativo para cada regra.
Como evito efeitos duplicados de triggers durante a virada?
Mantenha uma matriz de ativação com um único executor autorizado para cada regra do registro. Rode a lógica nova em sombra sem alterações compartilhadas e desative ou contorne o executor antigo quando o novo caminho começar a escrever.
Quando uma migração de PL/SQL está realmente concluída?
Ela termina quando os caminhos Oracle migrados podem ser desativados e a reprodução do tráfego e os casos planejados continuam aprovados. Todo trigger, job, permissão, sinônimo e conexão direta restante deve ter um responsável e um motivo explícitos.