O que é PL/SQL?

O PL/SQL é uma linguagem procedural projetada especificamente para incluir instruções SQL em sua sintaxe. Ele inclui elementos de linguagem procedural, como condições e loops, e pode tratar exceções (erros em tempo de execução).

O PL/SQL é nativo dos bancos de dados Oracle, e bancos de dados como IBM DB2, PostgreSQL e MySQL suportam construções PL/SQL por meio de recursos de compatibilidade.

O que é um UDF JavaScript?

O JavaScript UDF é a alternativa do Couchbase ao PL/SQL.

O JavaScript UDF traz a flexibilidade de scripts de uso geral do JavaScript para os bancos de dados, permitindo operações dinâmicas e avançadas em sistemas de bancos de dados modernos e aumentando a flexibilidade na consulta, no processamento e na transformação de dados.

A maioria dos bancos de dados modernos, como Couchbase, MongoDB, Snowflake e Google BigQuery, é compatível com JavaScript UDF.

O problema

Um problema comum visto pelos usuários que migram do Oracle para o Couchbase é a portabilidade de seus scripts PL/SQL. Em vez de oferecer suporte a PL/SQL, o Couchbase permite que os usuários construam funções definidas pelo usuário em JavaScript (com suporte desde 2021).

Os UDFs JavaScript permitem a manipulação fácil e intuitiva de dados de variantes e JSON. Os objetos variantes passados para um UDF são transformados em tipos e valores JavaScript nativos.

A consequência não intencional disso é que a maioria dos RDBMS existentes nos últimos dez anos incentivou fortemente os desenvolvedores a acessar o banco de dados usando suas extensões procedimentais para SQL (PL/pgSQL, PL/SQL), que oferecem suporte a construções procedimentais, integração com SQL, tratamento de erros, funções e procedimentos, acionadores e cursores ou, no mínimo, funções e procedimentos (como Sakila). Para qualquer tentativa de se afastar deles, todos os seus scripts precisariam ser reescritos.

A reescrita de código costuma ser uma tarefa tediosa, especialmente quando se trata de scripts PL/SQL que foram escritos nos anos 2000 e mantidos desde então. Esses scripts podem ser complexos, muitas vezes chegando a milhares de linhas, o que pode ser um fardo para o usuário corporativo médio.

Solução

A abordagem ideal seria desenvolver um avaliador PL/SQL totalmente novo, mas isso exigiria uma quantidade excessiva de horas de engenharia e, para o mesmo caso de uso, já temos um avaliador JSE moderno, estável e rápido.

Isso torna o problema um caso de uso perfeito para aproveitar os avanços contínuos em IA e LLMs. E foi isso que fizemos aqui. Usamos modelos de IA generativa para automatizar a conversão de PL/SQL para JSUDF.

A partir de junho de 2024, os modelos têm uma janela de contexto limitada, wo que significa que o PL/SQL mais longo é atingido pelo erro:  

Observe que isso é para o GPT4.

Então, esperamos que a IA se torne mais poderosa e permita mais tokens (como a Lei de Moore, mas para o comprimento de contexto versus precisão da IA)?

Não, é aí que ANTLR, a ferramenta geradora de analisadores. O ANTLR é conhecido por ser usado para o desenvolvimento de compiladores e interpretadores. Dessa forma, podemos dividir o script grande em unidades menores que podem ser traduzidas de forma independente.

Então, agora estamos construindo um transpilador? Bem, sim e não.

Estágios em um transpilador:

    1. Análise lexical (tokenização)
    2. Análise sintática (parsing)
    3. Análise semântica
    4. Geração de Representação Intermediária (RI)
    5. Otimização (opcional)
    6. Geração do código de destino

Como o tradutor de IA funciona

As etapas 1 e 2 acima são realizadas com o ANTLR. Usamos a interface Listener do ANTLR para pegar Procedimento/Função/Bloco anônimo individualpois são blocos de código independentes. No caso de o procedimento/função/bloco anônimo exceder a janela de contexto, traduzimos no nível da instrução (em que o LLM assume a existência do uso de variáveis/chamadas de função que não estão definidas aqui, mas em algum lugar anterior).

Posteriormente, as etapas 3, 4, 5 e 6 são deixadas para o LLM (por exemplo, GPT), ou seja, a tradução de cada bloco PL/SQL em uma função JavaScript da melhor forma possível, que também preserva a semântica operacional do bloco e é sintaticamente precisa.

Os resultados são surpreendentemente bastante positivos; a tradução é 80-85% precisa.

Outro benefício da solução é que reduzimos a alucinação ao nos concentrarmos em uma tarefa de cada vez, resultando em traduções mais precisas.

Para visualizar:

automate the conversion of PL/SQL to JSUDF

Como usar a ferramenta

O executável espera os seguintes argumentos de linha de comando:

-u: e-mail de login do capella
-p: senha de login do capella
-cpaddr: capella-url para a API de complementos de bate-papo
-orgid: ID da organização no caminho da API de chat-completions
-cbhost: node-ip: nó do cbcluster
-cbuser: nome do usuário do cluster: usuário do cbcluster, adicionado por meio do acesso ao banco de dados
-cbpassword: cluster-password: senha do cbcluster, adicionada por meio do acesso ao banco de dados
-cbport: porta tls do serviço de consulta: geralmente 18093
filepath , ou seja, o caminho para o script PL/SQL que deve ser traduzido
saída-> No diretório de saída, um arquivo com o mesmo nome do arquivo plsql é gerado com o código da biblioteca JavaScript traduzido.

Um exemplo:

cat exemplo1.sql

Para explicar brevemente o script acima:

    • Um loop externo é executado por 4 iterações, incrementando x em 1.000 e o contador em 1
    • O loop interno é executado por 4 iterações, incrementando x em 1 e o contador em 1

Executar o tradutor no PL/SQL de teste:

Saída JSUDF:

cat output/example1.js

O script traduzido tem uma função loop aninhado (nome gerado pelo LLM) que faz exatamente o que o bloco PL/SQL anônimo original especifica.

Observação: para funções/procedimentos nomeados, as funções JS traduzidas terão o mesmo nome. Para blocos anônimos, o LLM usa um nome que ele mesmo cria.

Problemas conhecidos

PL/SQL e JS são duas linguagens diferentes, e a forma como são suportadas no Oracle e no Couchbase não permite um mapeamento direto e limpo entre as duas. 

Abaixo estão algumas limitações que descobrimos e as soluções alternativas que implementamos para elas:

1. console.log não é compatível

DBMS_OUTPUT.PUT procedimento interno e outros 2 procedimentos internos semelhantes, DBMS_OUTPUT.PUT_LINE e DBMS_OUTPUT.NEW_LINE são traduzidos para console.log()mas console.log é uma API de navegador e não é compatível com a implementação de avaliação JavaScript do Couchbase. Essa tem sido uma pergunta frequente, considerando que a função de eventos do Couchbase é compatível com print() mas não em UDFs JavaScript.

Solução alternativa:

Espera-se que os usuários criem um registro balde.

Os registros são inseridos como parte de um documento INSERT into padrão.padrão coleta. O documento seria algo parecido com:

O usuário pode consultar seus registros selecionando em registro:

Um exemplo:

O PL/SQL original

Traduzido para JavaScript UDF

Isso já está implementado na ferramenta.


Para visualizar o registro:

2. Chamadas de função entre pacotes

Os procedimentos/funções listados na especificação do pacote são globais e podem ser usados em outros pacotes por meio de "package_name". "public_procedure/function". Mas o mesmo não se aplica a uma biblioteca JavaScript no Couchbase, pois as construções de importação-exportação não são compatíveis com a implementação de avaliação JavaScript do Couchbase.

Solução alternativa:

No caso de uma chamada de função entre bibliotecas "nome_da_biblioteca". "função"()Se o usuário tiver a biblioteca referenciada, espera-se que "lib_name" já criado; você pode verificar isso via GET /evaluator/v1/libraries

A função referenciada "function" (função) também deve ser criado como um UDF global; isso pode ser verificado por meio de GET /admin/functions_cache ou select system:functions keyspace. Dessa forma, podemos acessar a função via SQL++/N1QL.

Um exemplo:

Utilitários de matemática Pacote

show_sum Pacote

Código traduzido:

Ele é tratado automaticamente pelo programa, com um aviso de que deve ser verificado por um par de olhos humanos!

3. Variáveis globais

O PL/SQL suporta variáveis globais em nível de pacote e de sessão, mas as variáveis globais não são suportadas no JSUDF deliberadamente por design, pois isso causa preocupação com vazamentos de memória.

A solução alternativa sugerida requer ajustes manuais na tradução gerada. Por exemplo:


Qualquer função que modifique uma variável global deve aceitá-la como um argumento e retorná-la ao chamador.

increment_counter:

Qualquer função que apenas leia um global pode aceitá-lo como argumento.

show_globals:

Pacote para a biblioteca

Esta seção mostra uma conversão de pacote para biblioteca de ponta a ponta usando a ferramenta.

Exemplo de pacote PL/SQL:

Tradução:

Código:

Vamos inserir um novo documento de funcionário

Criar coleção de funcionários:

Inserir um funcionário:

Isso gera erros e Não tem problema podemos corrigi-lo manualmente.

Ler o motivo e exceção: Não é possível INSERIR a chave 1, que não é uma string, do tipo value.intValue, Ah! sempre se espera que a chave seja uma string, passando insert_employee("1", "joe", "briggs", 10000) seria suficiente, mas não é intuitivo esperar que o ID do funcionário para ser uma string.

Vamos alterar o código gerado:

E recriar o UDF:

Tentando inserir novamente:

Atualizar um funcionário:

Droga! Há um erro, o funcionário 1 não é o Joe, é a Emily. 

Vamos atualizar o funcionário 1

Ver o funcionário:

Excluir o funcionário:

Emily saiu.

Novamente um erro com o código gerado. Observando o motivo e a exceção, podemos confirmar que o código traduzido inclui a exclusão em uma transação, o que não era o caso no original.

Para transações, os buckets precisam ter durabilidade mas isso requer mais de um servidor de dados, por isso o erro.

A correção aqui é alterar o código para remover a tradução anexa:

Agora, todas as funções do PL/SQL original funcionam no Couchbase por meio de UDFs JavaScript. Sim, o exemplo é bastante trivial, mas você entendeu a essência de como usar a ferramenta para migrar seus scripts PL/SQL com pouca supervisão manual.

Lembre-se de que a ferramenta deve lhe fornecer 80%, os outros 20% ainda precisam ser feitos por você, mas é muito melhor do que escrever todo esse código por conta própria!

O futuro

Este projeto é de código aberto, portanto, sinta-se à vontade para contribuir. Algumas ideias que foram sugeridas:

    1. IA crítica que pode criticar o código gerado para garantir que a intervenção manual não seja necessária
    2. Atualmente, o código-fonte é um código que simplesmente funciona; nenhuma ideia de paralelismo ou reutilização de código foi colocada em prática.

E também inclui as limitações discutidas anteriormente.

Recursos

Por fim, gostaria de agradecer a Kamini Jagtiani por me orientar e Pierre Regazzoni por me ajudar a testar a ferramenta de conversão.

Autor

Postado por Gaurav Jayaraj - Engenheiro de software

Gaurav Jayaraj é estagiário na equipe de consultas da Couchbase R&D. Gaurav está cursando bacharelado em Ciência da Computação na PES University, Bangalore.

Deixar uma resposta