Buenas prácticas y tutoriales

Una herramienta para facilitar la transición de Oracle PL/SQL a Couchbase JavaScript UDF

¿Qué es PL/SQL?

PL/SQL es un lenguaje procedimental diseñado específicamente para incluir sentencias SQL en su sintaxis. Incluye elementos de lenguaje procedimental como condiciones y bucles, y puede manejar excepciones (errores en tiempo de ejecución).

PL/SQL es nativo de las bases de datos Oracle, y bases de datos como IBM DB2, PostgreSQL y MySQL admiten construcciones PL/SQL a través de funciones de compatibilidad.

¿Qué es una UDF de JavaScript?

JavaScript UDF es la alternativa de Couchbase a PL/SQL.

JavaScript UDF traslada la flexibilidad de las secuencias de comandos de uso general de JavaScript a las bases de datos, lo que permite realizar operaciones dinámicas y potentes en los sistemas de bases de datos modernos y mejora la flexibilidad en la consulta, el procesamiento y la transformación de datos.

La mayoría de las bases de datos modernas como Couchbase, MongoDB, Snowflake y Google BigQuery soportan JavaScript UDF.

El problema

Un problema común visto por los usuarios que migran de Oracle a Couchbase es portar sus scripts PL/SQL. En lugar de soportar PL/SQL, Couchbase permite a los usuarios construir funciones definidas por el usuario en JavaScript (soportado desde 2021).

Las UDF de JavaScript permiten una manipulación fácil e intuitiva de los datos de variantes y JSON. Los objetos variantes pasados a una UDF se transforman en tipos y valores nativos de JavaScript.

La consecuencia involuntaria de esto es que la mayoría de los RDBMS que han existido en los últimos diez años han animado encarecidamente a los desarrolladores a acceder a la base de datos utilizando sus extensiones procedimentales de SQL (PL/pgSQL, PL/SQL), que soportan construcciones procedimentales, integración con SQL, gestión de errores, funciones y procedimientos, disparadores y cursores, o como mínimo, funciones y procedimientos (como Sakila). Para cualquier intento de alejarse de ellos, habría que reescribir todos sus scripts.

Reescribir código es a menudo una tarea tediosa, especialmente cuando se trata de scripts PL/SQL que han sido escritos en la década de 2000 y mantenidos desde entonces. Estos scripts pueden ser complejos, a menudo extendiéndose a miles de líneas, lo que puede ser abrumador para el usuario medio de la empresa.

Solución

Lo ideal sería desarrollar un evaluador PL/SQL completamente nuevo, pero eso requeriría una cantidad excesiva de horas de ingeniería y, para el mismo caso de uso, ya disponemos de un JSEvaluator moderno, estable y rápido.

Esto convierte el problema en un caso de uso perfecto para aprovechar los avances actuales en IA y LLM. Y eso es lo que hemos hecho aquí. Hemos utilizado modelos de IA generativa para automatizar la conversión de PL/SQL a JSUDF.

A partir de junio de 2024, los modelos tienen una ventana de contexto limitada, which significa PL/SQL más largo se golpean con el error:  

Tenga en cuenta que esto es para GPT4.

Entonces, ¿esperamos a que la IA sea más potente y permita más fichas (como la Ley de Moore, pero para el contexto-longitud-vs-precisión de la IA)?

No, ahí es donde ANTLR, a herramienta generadora de analizadores sintácticos. ANTLR es bien conocido por ser utilizado para el desarrollo de compiladores e intérpretes. De esta manera podemos dividir el script grande en unidades más pequeñas que se pueden traducir de forma independiente.

Entonces, ¿estamos construyendo ¿transpilador? Bueno, sí y no.

Etapas de un transpilador:

    1. Análisis léxico (tokenización)
    2. Análisis sintáctico (Parsing)
    3. Análisis semántico
    4. Generación de representaciones intermedias (RI)
    5. Optimización (opcional)
    6. Generación de código de destino

Cómo funciona el traductor de IA

Los pasos 1,2 anteriores se realizan utilizando ANTLR. Utilizamos la interfaz Listener de ANTLR para agarrar procedimiento/función/bloque anónimo individualya que son bloques de código independientes. En el caso de que el procedimiento/función/bloque anónimo exceda por sí mismo la ventana de contexto, traducimos a nivel de sentencia (donde el LLM asume la existencia de uso de variables/llamadas a funciones que no están definidas aquí sino en algún lugar anterior).

Posteriormente, los pasos 3, 4, 5 y 6 se dejan en manos del LLM (por ejemplo, GPT), es decir, traducir cada bloque PL/SQL en una función JavaScript lo mejor posible que también preserve la semántica operativa del bloque y sea sintácticamente precisa.

Los resultados son sorprendentemente positivos; la traducción es 80-85% exacta.

Otra ventaja de la solución es que reducimos las alucinaciones al centrarnos en una tarea cada vez, lo que se traduce en traducciones más precisas.

Para visualizar:

automate the conversion of PL/SQL to JSUDF

Cómo utilizar la herramienta

El ejecutable espera los siguientes argumentos de línea de comandos:

-u: capella signin email
-p: contraseña de acceso a capella
-cpaddr: capella-url para chat-completions api
-orgid: id de la organización en la ruta de la api de chat-completions
-cbhost: nodo-ip: nodo cbcluster
-cbuser: cluster-user-name: usuario cbcluster, añadido a través de database-acess
-cbpassword: cluster-password: contraseña del cbcluster, añadida a través del acceso a la base de datos.
-cbport: query-service tls port: normalmente 18093
filepath , es decir, la ruta del script PL/SQL que debe traducirse
salida-> En el directorio de salida, se creará un archivo con el mismo nombre que el archivo plsql con el código traducido de la biblioteca JavaScript.

Un ejemplo:

cat ejemplo1.sql

Para explicar brevemente el guión anterior:

    • Un bucle externo se ejecuta durante 4 iteraciones, incrementando x en 1000, contador en 1
    • El bucle interno se ejecuta durante 4 iteraciones, incrementando x en 1, contador en 1

Ejecutar el traductor en el PL/SQL de prueba:

Salida JSUDF:

cat salida/ejemplo1.js

El script traducido tiene una función bucle anidado (nombre generado por LLM) que hace exactamente lo que especifica el bloque PL/SQL anónimo original.

Nota al margen: para funciones/procedimientos con nombre, las funciones JS traducidas tendrán el mismo nombre. Para bloques anónimos LLM utiliza un nombre que se le ocurre.

Problemas conocidos

PL/SQL y JS son 2 lenguajes diferentes, y la forma en que están soportados en Oracle y Couchbase no permite un mapeo directo entre los 2. 

A continuación se indican algunas de las limitaciones que hemos descubierto y las soluciones que hemos aplicado:

1. console.log no es compatible

DBMS_OUTPUT.PUT y otros 2 similares, DBMS_OUTPUT.PUT_LINE y DBMS_OUTPUT.NEW_LINE se traducen a console.log()pero console.log es una API de navegador y no está soportada por la implementación de evaluación JavaScript de Couchbase. Esta ha sido una pregunta frecuente, considerando que la función de eventos de Couchbase sí soporta imprimir() pero no en las UDF de JavaScript.

Solución:

Se espera que los usuarios creen un registro cubo.

Los registros se insertan como parte de un documento INSERT en por defecto.por defecto colección. El documento sería algo así

El usuario puede consultar sus registros seleccionando en registro:

Un ejemplo:

El PL/SQL original

Traducido a JavaScript UDF

Esto ya está implementado en la herramienta.


Para ver el registro:

2. Llamadas a funciones entre paquetes

Los procedimientos/funciones enumerados en la especificación del paquete son globales y pueden utilizarse desde otros paquetes mediante "nombre_paquete". "procedimiento/funcion_publica". Pero no ocurre lo mismo con una librería JavaScript en Couchbase, ya que las construcciones de importación-exportación no están soportadas por la implementación de evaluación JavaScript de Couchbase.

Solución:

En caso de llamada a una función interbibliotecaria "lib_name". "function"()se espera que el usuario disponga de la biblioteca de referencia "lib_name" ya creado; puede comprobarlo mediante GET /evaluator/v1/bibliotecas

La función de referencia "función" también se espera que sea creada como una UDF global; esto puede ser verificado vía GET /admin/functions_cache o select system:functions keyspace. De esta forma podemos acceder a la función a través de SQL++/N1QL.

Un ejemplo:

utilidades_matemáticas Paquete

mostrar_suma Paquete

Código traducido:

El programa lo gestiona automáticamente, con la advertencia de que debe ser verificado por una persona.

3. Variables globales

PL/SQL soporta variables globales a nivel de paquete y a nivel de sesión, pero las variables globales no están soportadas en JSUDF deliberadamente por diseño ya que esto causa preocupación por fugas de memoria.

La solución sugerida requiere un ajuste manual de la traducción generada. Por ejemplo:


Cualquier función que modifique una variable global debe aceptarla como argumento y devolverla a quien la llama.

contador_de_incrementos:

Cualquier función que sólo lea un global puede aceptarlo como argumento.

mostrar_globales:

Paquete a la biblioteca

Esta sección muestra una conversión de paquete a biblioteca de extremo a extremo utilizando la herramienta.

Paquete PL/SQL de ejemplo:

Traducción:

Código:

Insertemos un nuevo documento de empleado

Crear colección de empleados:

Inserta un empleado:

Esto da error, y Está bien. podemos arreglarlo manualmente.

Lectura del motivo y excepción: No se puede INSERTAR la clave 1 del tipo valor.intValor, ¡Ah! siempre se espera que la clave sea una cadena, pasando insert_employee("1", "joe", "briggs", 10000) sería suficiente, pero no es intuitivo esperar que empleado_id sea una cadena.

Modifiquemos el código generado:

Y vuelva a crear la UDF:

Intentando insertar de nuevo:

Actualizar a un empleado:

¡Dispara! Hay un error, el empleado 1 no es Joe, es Emily. 

Actualicemos al empleado 1

Ver al empleado:

Elimina al empleado:

Emily se fue.

De nuevo un error con el código generado, mirando la razón y la excepción podemos confirmar que el código traducido encierra delete en una transacción, lo que no era el caso en el original.

Para las transacciones, los buckets deben tener durabilidad pero esto requiere más de un servidor de datos, de ahí el error.

La solución consiste en modificar el código para eliminar la traducción adjunta:

Ahora, todas las funciones en el PL/SQL original funcionan en Couchbase a través de JavaScript UDFs. Sí, el ejemplo es bastante trivial, pero puedes entender cómo usar la herramienta para migrar tus scripts PL/SQL con poca supervisión manual.

Recuerda que se supone que la herramienta te llevará 80%, los otros 20% aún los tienes que hacer tú, ¡pero mucho mejor que escribir todo ese código tú mismo!

El futuro

Este proyecto es de código abierto, así que siéntete libre de contribuir. Algunas ideas que se han sugerido:

    1. IA crítica que puede criticar el código generado para garantizar que la intervención manual no sea necesaria en absoluto.
    2. Actualmente, el código fuente es un código que simplemente funciona; no se ha pensado en el paralelismo ni en la reutilización del código.

Y también incluye las limitaciones comentadas anteriormente.

Recursos

Por último, me gustaría dar las gracias a Kamini Jagtiani por guiarme y Pierre Regazzoni por ayudarme a probar la herramienta de conversión.

Comparte este artículo
Recibe actualizaciones del blog de Couchbase en tu bandeja de entrada
Este campo es obligatorio.

Author

Posted by Gaurav Jayaraj - Ingeniero de software

Gaurav Jayaraj es becario en el equipo de consultas de Couchbase R&D. Gaurav es licenciado en Informática por la Universidad PES de Bangalore.

Deja un comentario

¿Listo para empezar con Couchbase Capella?

Empezar a construir

Consulte nuestro portal para desarrolladores para explorar NoSQL, buscar recursos y empezar con tutoriales.

Utilizar Capella gratis

Ponte manos a la obra con Couchbase en unos pocos clics. Capella DBaaS es la forma más fácil y rápida de empezar.

Póngase en contacto

¿Quieres saber más sobre las ofertas de Couchbase? Permítanos ayudarle.