{"id":16846,"date":"2025-02-11T08:52:20","date_gmt":"2025-02-11T16:52:20","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=16846"},"modified":"2025-07-08T09:15:37","modified_gmt":"2025-07-08T16:15:37","slug":"plsql-to-javascript-udf-conversion-tool","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/","title":{"rendered":"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF"},"content":{"rendered":"<h2><span style=\"font-weight: 400;\">What is PL\/SQL?<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">PL\/SQL is a procedural language designed specifically to embrace SQL statements within its syntax. It includes procedural language elements such as conditions and loops, and can handle exceptions (run-time errors).<\/span><\/p>\n<p><span style=\"font-weight: 400;\">PL\/SQL is native to Oracle databases, and databases like IBM DB2, PostgreSQL, and MySQL support PL\/SQL constructs through compatibility features.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">What is a JavaScript UDF?<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">JavaScript UDF is Couchbase\u2019s alternative to PL\/SQL.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">JavaScript UDF brings JavaScript&#8217;s general-purpose scripting flexibility to databases, allowing for dynamic and powerful operations across modern database systems and enhances flexibility in data querying, processing, and transformation.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Most modern databases like Couchbase, MongoDB, Snowflake, and Google BigQuery support JavaScript UDF.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">The problem<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">A common problem seen by users migrating from Oracle to Couchbase is porting their PL\/SQL scripts. Instead of supporting PL\/SQL, Couchbase lets users construct user-defined functions in JavaScript (supported since 2021).<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">JavaScript UDFs allow easy, intuitive manipulation of variant and JSON data. Variant objects passed to a UDF are transformed to native JavaScript types and values.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The unintended consequence of this is that the majority of RDBMS that have been in existence for the last ten years have strongly encouraged developers to access the database using their procedural extensions to SQL (PL\/pgSQL, PL\/SQL), which support procedural constructs, integration with SQL, error handling, functions and procedures, triggers, and cursors, or at the very least, functions and procedures (like Sakila). For any attempt to move away from them, all of their scripts would need to be rewritten.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Rewriting code is often a tedious task, especially when dealing with PL\/SQL scripts that have been written in the 2000s and maintained since then. These scripts can be complex, often extending to thousands of lines, which can be overwhelming for the average enterprise user.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Solution<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">The ideal approach would be to develop a whole new PL\/SQL evaluator, but that would require an excessive amount of engineering hours, and for the same use case, we already have a modern, stable, and fast JSEvaluator; So why support another evaluator?<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This makes the problem a perfect use-case to leverage the ongoing advances in AI and LLMs. And that&#8217;s what we have done here. We have used Generative AI models to <\/span><b>automate the conversion of PL\/SQL to JSUDF<\/b><span style=\"font-weight: 400;\">.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">As of June 2024, <a href=\"https:\/\/platform.openai.com\/docs\/models\/gpt-4-turbo-and-gpt-4\">models have a limited context window<\/a>, w<\/span><span style=\"font-weight: 400;\">hich means longer PL\/SQL get hit with the error:\u00a0\u00a0<\/span><\/p>\n<pre class=\"nums:false wrap:true lang:default decode:true\">This model's maximum context length is 8192 tokens. However, your messages resulted in &lt;More-than-8192&gt; tokens. Please reduce the length of the messages.<\/pre>\n<p><span style=\"font-weight: 400;\">Note, that this is for GPT4.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">So do we wait for AI to become more powerful and allow more tokens (like Moore\u2019s Law but for the AI\u2019s context-length-vs-precision)?<\/span><\/p>\n<p><span style=\"font-weight: 400;\">No, that\u2019s where <a href=\"https:\/\/www.antlr.org\/\">ANTLR<\/a>, a<\/span><span style=\"font-weight: 400;\"> parser generator tool, comes in. ANTLR is well-known to be used for Compiler and Interpreter Development. That way we can break the big script to smaller units that can be translated independently.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">So are we now building a <\/span><b>transpiler? <\/b><span style=\"font-weight: 400;\">Well, yes and no.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><strong>Stages in a transpiler:<\/strong><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Lexical Analysis (Tokenization)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Syntactic Analysis (Parsing)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Semantic Analysis<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Intermediate Representation (IR) Generation<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Optimization (Optional)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Target Code Generation<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<h4><strong><span style=\"font-size: 19px;\">How the AI translator works<\/span><\/strong><\/h4>\n<p><span style=\"font-weight: 400;\">Above steps 1,2 are done using ANTLR.\u00a0<\/span><span style=\"font-weight: 400;\">We use ANTLR\u2019s Listener interface to <\/span><b>grab individual Procedure\/Function\/Anonymous-block<\/b><span style=\"font-weight: 400;\">,\u00a0 as they are independent blocks of code. In a case where the\u00a0 Procedure\/Function\/Anonymous-block are themselves exceeding the context window, we translate at a statement level (where the LLM assumes the existence of use of variables\/function calls that aren\u2019t defined here but somewhere before).<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Subsequently, steps 3, 4, 5, and 6 are left to the LLM (e.g., GPT), i.e., translating each PL\/SQL block into a JavaScript function to the best of its ability that also preserves the operational semantics of the block and is syntactically accurate.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The results are surprisingly quite positive; the translation is 80-85% accurate.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Another benefit of the solution is that we reduce hallucination by focusing on one task at a time, resulting in more accurate translations.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">To visualize:<\/span><\/p>\n<p><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-16847 size-large\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1-1024x904.png\" alt=\"automate the conversion of PL\/SQL to JSUDF\" width=\"900\" height=\"795\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1-1024x904.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1-300x265.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1-768x678.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1-1536x1356.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1-1320x1166.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1.png 1787w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/a><\/p>\n<h2><span style=\"font-weight: 400;\">How to use the tool<\/span><\/h2>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><span style=\"font-weight: 400;\">Download the executable from <a href=\"https:\/\/github.com\/couchbaselabs\/plsql-to-jsudf2\/releases\/tag\/v1.0.0\">Couchbase Labs GitHub<\/a> and access the <a href=\"https:\/\/github.com\/couchbaselabs\/plsql-to-jsudf2\/blob\/master\/README.md\">README<\/a>.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">The executable expects the following command-line arguments:<\/span><\/p>\n<p style=\"padding-left: 40px;\"><span style=\"font-weight: 400;\">-u: capella signin email<br \/>\n<\/span><span style=\"font-weight: 400;\">-p: capella signin password<br \/>\n<\/span><span style=\"font-weight: 400;\">-cpaddr: capella-url for chat-completions api<br \/>\n<\/span><span style=\"font-weight: 400;\">-orgid: organisation id in the chat-completions api path<br \/>\n<\/span><span style=\"font-weight: 400;\">-cbhost: node-ip: cbcluster node<br \/>\n<\/span><span style=\"font-weight: 400;\">-cbuser: cluster-user-name: cbcluster user, added through database-acess<br \/>\n<\/span><span style=\"font-weight: 400;\">-cbpassword: cluster-password: cbcluster password, added through database-access<br \/>\n<\/span><span style=\"font-weight: 400;\">-cbport: query-service tls port: usually 18093<br \/>\n<\/span><span style=\"font-weight: 400;\">filepath , i.e path to the PL\/SQL script that has to be translated<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">output-&gt;\u00a0<\/span><span style=\"font-weight: 400;\">In the output directory, a file with the same name as the <em>plsql<\/em> file is generated with translated JavaScript Library code.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">An example:<\/span><\/p>\n<p><code>cat example1.sql<\/code><\/p>\n<pre class=\"nums:false lang:default decode:true\">DECLARE\r\n\u00a0\u00a0\u00a0x NUMBER := 0;\r\n\u00a0\u00a0\u00a0counter NUMBER := 0;\r\nBEGIN\r\n\u00a0\u00a0\u00a0FOR i IN 1..4 LOOP\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0x := x + 1000;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0counter := counter + 1;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0INSERT INTO temp VALUES (x, counter, 'in OUTER loop');\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0--start an inner block\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0DECLARE\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0x NUMBER := 0;\u00a0 -- this is a local version of x\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0BEGIN\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0FOR i IN 1..4 LOOP\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0x := x + 1;\u00a0 -- this increments the local x\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0counter := counter + 1;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0INSERT INTO temp VALUES (x, counter, 'inner loop');\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END LOOP;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END;\r\n\u00a0\u00a0\u00a0END LOOP;\r\n\u00a0\u00a0\u00a0COMMIT;\r\nEND;<\/pre>\n<p><span style=\"font-weight: 400;\">To briefly explain the above script:<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><span style=\"font-weight: 400;\">An outer loop runs for 4 iterations, incrementing x by 1000, counter by 1<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/li>\n<li><span style=\"font-weight: 400;\">Inner loop runs for 4 iterations, incrementing x by 1, counter by 1<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Running the translator on the test PL\/SQL:<\/span><\/p>\n<pre class=\"nums:false wrap:true lang:default decode:true\">.\/plsql-to-jsudf -u \u00abcapella-signin-mailid\u00bb -p \u00abcapella-signin-password\u00bb -cpaddr https:\/\/api.cloud.couchbase.com -orgid \u00abcapella-organisation-id\u00bb -cbhost \u00abhostname of data node\u00bb -cbuser \u00abcbcluster username\u00bb -cbpassword \u00abcbcluster password\u00bb\u00a0 -cbport 18093 .\/translator\/test\/plsql\/example1.sql\r\n<\/pre>\n<p><span style=\"font-weight: 400;\">Output JSUDF:<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><code>cat output\/example1.js<\/code><\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false wrap:true lang:js decode:true\">function nestedloop(){\r\n\u00a0\u00a0\u00a0\u00a0var x = 0;\r\n\u00a0\u00a0\u00a0\u00a0var counter = 0;\r\n\u00a0\u00a0\u00a0\u00a0var querybegin = BEGIN WORK;\r\n\u00a0\u00a0\u00a0\u00a0querybegin.close();\r\n\u00a0\u00a0\u00a0\u00a0for (var i = 1; i &lt;= 4; i++){\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0x = x + 1000;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0counter = counter + 1;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var params = [x, counter];\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var query = N1QL('INSERT INTO test.testscope.temp VALUES (uuid(),{\"val1\":$1,\"val2\":$2,\"val3\":\"in OUTER loop\"})',params);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0query.close();\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var x_inner = 0;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0for (var j = 1; j &lt;= 4; j++){\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0x_inner = x_inner + 1;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0counter = counter + 1;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var params_inner = [x_inner, counter];\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var query_inner = N1QL('INSERT INTO test.testscope.temp VALUES (uuid(),{\"val1\":$1,\"val2\":$2,\"val3\":\"inner loop\"})',params_inner);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0query_inner.close();\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0var querycommit = COMMIT WORK;\r\n\u00a0\u00a0\u00a0\u00a0querycommit.close();\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">The translated script has a function <em>nestedloop<\/em> (name generated by LLM) that does exactly what the original anonymous PL\/SQL block specifies.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Side note: for named functions\/procedures translated JS functions will have the same name.\u00a0 For anonymous blocks LLM uses a name it comes up with.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Known issues<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">PL\/SQL and JS are 2 different languages, and the way they are supported in Oracle and Couchbase doesn\u2019t allow for a clean direct mapping between the 2.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Below are some limitations we discovered and the workarounds we have implemented for the same:<\/span><\/p>\n<h4><span style=\"font-weight: 400;\">\u200b\u200b1. console.log is not supported<\/span><\/h4>\n<p><span style=\"font-weight: 400;\"><em>DBMS_OUTPUT.PUT<\/em> builtin procedure and other 2 similar builtins, <em>DBMS_OUTPUT.PUT_LINE<\/em> and <em>DBMS_OUTPUT.NEW_LINE<\/em> are translated to <em>console.log()<\/em>, but console.log is a browser API and is not supported by Couchbase&#8217;s JavaScript evaluation implementation. This has been a frequent ask, considering the Couchbase eventing function does support <em>print()<\/em> statements but not in JavaScript UDFs.<\/span><\/p>\n<p><b>Workaround:<\/b><\/p>\n<p><span style=\"font-weight: 400;\">Users are expected to create a <em>logging<\/em>\u00a0bucket.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Logs are inserted as part of a document INSERT into `default`.`default` collection. The document would look something like:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">{\r\n\u00a0\u00a0\u00a0\"udf\": \u00abfunc-name\u00bb,\r\n\u00a0\u00a0\u00a0\"log\": \u00abargument to console.log\u00bb, \/\/ the actual log line\r\n\u00a0\u00a0\u00a0\"time\": \u00abcurrent ISO time string\u00bb\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">User can lookup his logs by selecting on logging:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">SELECT * FROM logging WHERE udf= \"\u00abfunc-name\u00bb\";\r\nSELECT * FROM logging WHERE time BETWEEN \"\u00abdate1\u00bb\" AND \"\u00abdate2\u00bb\";\r\n<\/pre>\n<p><span style=\"font-weight: 400;\">An example:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The original PL\/SQL<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">BEGIN\r\n\u00a0\u00a0\u00a0DBMS.OUTPUT.PUT(\"Hello world!\");\r\nEND;<\/pre>\n<p><span style=\"font-weight: 400;\">Translated to JavaScript UDF<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">function helloWorld() {\r\n\u00a0 \u00a0\/\/ workaround for console.log(\"Hello world!\");\r\n\u00a0\u00a0\u00a0var currentDate = new Date();\r\n\u00a0\u00a0\u00a0var utcISOString = currentDate.toISOString();\r\n\u00a0\u00a0\u00a0var params = [utcISOString,'anonymousblock1',\"Hello world!\"];\r\n\u00a0\u00a0\u00a0var logquery = N1QL('INSERT INTO logging VALUES(UUID(),{\"udf\":$2, \"log\":$3, \"time\":$1}, {\"expiration\": 5*24*60*60 })', params);\r\n\u00a0\u00a0\u00a0logquery.close();\r\n}\r\n<\/pre>\n<p><span style=\"font-weight: 400;\">This is already implemented in the tool.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">To view the log:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">EXECUTE FUNCTION helloWorld();\r\n\"results\": [\r\n\u00a0\u00a0\u00a0null\r\n]\r\n\r\nCREATE PRIMARY INDEX ON logging;\r\n\"results\": [\r\n]\r\n\r\nSELECT * FROM logging;\r\n\"results\": [\r\n\u00a0\u00a0\u00a0{\"logging\":{\"log\":\"Hello world!\",\"time\":\"2024-06-26T09:20:56.000Z\",\"udf\":\"anonymousblock1\"}}\r\n]<\/pre>\n<h4><span style=\"font-weight: 400;\">2. Cross Package function calls<\/span><\/h4>\n<p><span style=\"font-weight: 400;\">Procedures\/Functions listed in the package specification are Global, and can be used from other packages via <em>\u00abpackage_name\u00bb.\u00abpublic_procedure\/function\u00bb<\/em>. But the same is not true for a JavaScript Library in Couchbase, as import-export constructs are not supported by Couchbase&#8217;s JavaScript evaluation implementation.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><b>Workaround:<\/b><b><\/b><\/p>\n<p><span style=\"font-weight: 400;\">In case of an interlibrary function call<em> \u00ablib_name\u00bb.\u00abfunction\u00bb()<\/em>, user is expected to have the referenced library <em>\u00ablib_name\u00bb<\/em> already created; you can verify this via <em>GET \/evaluator\/v1\/libraries<\/em><\/span><\/p>\n<p><span style=\"font-size: 19px;\">The referenced function <em>\u00abfunction\u00bb<\/em> also is expected to be created as a global UDF; this can be verified via GET \/admin\/functions_cache or select system:functions keyspace. This way we can access the function via SQL++\/N1QL.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">An example:<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><em>math_utils<\/em> Package<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">CREATE OR REPLACE PACKAGE math_utils AS\r\n\u00a0\u00a0\u00a0-- Public function to add two numbers\r\n\u00a0\u00a0\u00a0FUNCTION add_numbers(p_num1 NUMBER, p_num2 NUMBER) RETURN NUMBER;\r\nEND math_utils;\r\n\/\r\n\r\nCREATE OR REPLACE PACKAGE BODY math_utils AS\r\n\u00a0\u00a0\u00a0FUNCTION add_numbers(p_num1 NUMBER, p_num2 NUMBER) RETURN NUMBER IS\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0BEGIN\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0RETURN p_num1 + p_num2;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0END add_numbers;\r\nEND math_utils;\r\n\/\r\n<\/pre>\n<p><span style=\"font-weight: 400;\"><em>show_sum<\/em> Package<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">CREATE OR REPLACE PACKAGE show_sum AS\r\n\u00a0\u00a0\u00a0-- Public procedure to display the sum of two numbers\r\n\u00a0\u00a0\u00a0PROCEDURE display_sum(p_num1 NUMBER, p_num2 NUMBER);\r\nEND show_sum;\r\n\/\r\n\r\nCREATE OR REPLACE PACKAGE BODY show_sum AS\r\n\u00a0\u00a0\u00a0PROCEDURE display_sum(p_num1 NUMBER, p_num2 NUMBER) IS\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0v_sum NUMBER;\r\n\u00a0\u00a0\u00a0BEGIN\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0-- Calling the add_numbers function from math_utils package\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0v_sum := math_utils.add_numbers(p_num1, p_num2);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0-- Displaying the sum using DBMS_OUTPUT.PUT_LINE\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0DBMS_OUTPUT.PUT_LINE('The sum of ' || p_num1 || ' and ' || p_num2 || ' is ' || v_sum);\r\n\u00a0\u00a0\u00a0END display_sum;\r\nEND show_sum;\r\n\/<\/pre>\n<p><span style=\"font-weight: 400;\">Translated code:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true \">function show_sum(a, b) {\r\n var sum_result;\r\n\r\n\/\/ Workaround for cross library function call math_utils.add_numbers(a, b)\r\nvar crossfunc = N1QL(\"EXECUTE FUNCTION add_numbers($1,$2)\",[a, b])\r\nvar crossfuncres = []\r\nfor(const doc of crossfunc) {\r\n\u00a0\u00a0\u00a0crossfuncres.push(doc);\r\n}\r\n\r\n\/\/ actual replacement for math_utils.add_numbers(a, b)\r\nsum_result = crossfuncres[0];\r\n\r\n\/\/ workaround for console.log('The sum of ' + a + ' and ' + b + ' is: ' + sum_result);\r\nvar currentDate = new Date();\r\nvar utcISOString = currentDate.toISOString();\r\nvar params = [utcISOString,'SHOW_SUM','The sum of ' + a + ' and ' + b + ' is: ' + sum_result];\r\nvar logquery = N1QL('INSERT INTO logging VALUES(UUID(),{\"udf\":$2, \"log\":$3, \"time\":$1}, {\"expiration\": 5*24*60*60 })', params);\r\nlogquery.close();\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">It is auto-handled by the program, with a warning that it should be verified by a human set of eyes!<\/span><\/p>\n<h4><span style=\"font-weight: 400;\">3. Global Variables<\/span><\/h4>\n<p><span style=\"font-weight: 400;\">PL\/SQL supports package level and session level global variables, but global variables are not supported in JSUDF deliberately by design as this causes concern for memory leaks.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Suggested workaround requires manual tweaking of the generated translation. For example:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">  CREATE OR REPLACE PACKAGE global_vars_pkg AS\r\n\u00a0\u00a0\u00a0\u00a0\u00a0-- Global variable declarations\r\n\u00a0\u00a0\u00a0\u00a0\u00a0g_counter NUMBER := 0;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0g_message VARCHAR2(100) := 'Initial Message';\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0-- Public procedure declarations\r\n\u00a0\u00a0\u00a0\u00a0\u00a0PROCEDURE increment_counter;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0PROCEDURE set_message(p_message VARCHAR2);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0PROCEDURE show_globals;\r\n\u00a0\u00a0\u00a0END global_vars_pkg;\r\n\u00a0\u00a0\u00a0\/\r\n\r\n\u00a0\u00a0\u00a0CREATE OR REPLACE PACKAGE BODY global_vars_pkg AS\r\n\r\n \u00a0\u00a0\u00a0\u00a0-- Procedure to increment the counter\r\n\u00a0\u00a0\u00a0\u00a0\u00a0PROCEDURE increment_counter IS\r\n\u00a0\u00a0\u00a0\u00a0\u00a0BEGIN\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0g_counter := g_counter + 1;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0END increment_counter;\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0-- Procedure to set the global message\r\n\u00a0\u00a0\u00a0\u00a0\u00a0PROCEDURE set_message(p_message VARCHAR2) IS\r\n\u00a0\u00a0\u00a0\u00a0\u00a0BEGIN\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0g_message := p_message;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0END set_message;\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0-- Procedure to display the current values of global variables\r\n\u00a0\u00a0\u00a0\u00a0\u00a0PROCEDURE show_globals IS\r\n\u00a0\u00a0\u00a0\u00a0\u00a0BEGIN\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0DBMS_OUTPUT.PUT_LINE('g_counter = ' || g_counter);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0DBMS_OUTPUT.PUT_LINE('g_message = ' || g_message);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0END show_globals;\r\n\r\n\u00a0\u00a0\u00a0END global_vars_pkg;\r\n\u00a0\u00a0\u00a0\/<\/pre>\n<p><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">Any function\u00a0 that modifies a global variable must accept it as an argument and return it to the caller.<\/span><\/p>\n<p><span style=\"font-size: 19px;\"><em>increment_counter<\/em>:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">function increment_counter(counter){\r\n\u00a0\u00a0\u00a0counter = counter + 1;\r\n\u00a0\u00a0\u00a0return counter\r\n}\r\n<\/pre>\n<p><span style=\"font-weight: 400;\">Any function that only reads a global can accept it as an argument.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><em>show_globals<\/em>:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">function show_globals(counter, message){\r\n\u00a0\u00a0\u00a0\/\/ workaround for console.log(counter);\r\n\u00a0\u00a0\u00a0var currentDate = new Date();\r\n\u00a0\u00a0\u00a0var utcISOString = currentDate.toISOString();\r\n\u00a0\u00a0\u00a0var params = [utcISOString,'SHOW_GLOBALS',couter];\r\n\u00a0\u00a0\u00a0var logquery = N1QL('INSERT INTO logging VALUES(UUID(),{\"udf\":$2, \"log\":$3, \"time\":$1}, {\"expiration\": 5*24*60*60 })', params);\r\n\u00a0\u00a0\u00a0logquery.close();\r\n\r\n\u00a0\u00a0\u00a0\/\/ workaround for console.log(message);\r\n\u00a0\u00a0\u00a0var currentDate = new Date();\r\n\u00a0\u00a0\u00a0var utcISOString = currentDate.toISOString();\r\n\u00a0\u00a0\u00a0var params = [utcISOString,'SHOW_GLOBALS',message];\r\n\u00a0\u00a0\u00a0var logquery = N1QL('INSERT INTO logging VALUES(UUID(),{\"udf\":$2, \"log\":$3, \"time\":$1}, {\"expiration\": 5*24*60*60 })', params);\r\n\u00a0\u00a0\u00a0logquery.close();\r\n}\r\n<\/pre>\n<h3><span style=\"font-weight: 400;\">Package to Library<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">This section shows an end-to-end package-to-library conversion using the tool.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Sample PL\/SQL package:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">CREATE OR REPLACE PACKAGE emp_pkg IS\r\n\u00a0\u00a0PROCEDURE insert_employee(\r\n\u00a0\u00a0\u00a0\u00a0p_emp_id \u00a0 \u00a0 IN employees.emp_id%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_first_name IN employees.first_name%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_last_name\u00a0 IN employees.last_name%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_salary \u00a0 \u00a0 IN employees.salary%TYPE\r\n\u00a0\u00a0);\r\n\r\n\u00a0\u00a0PROCEDURE update_employee(\r\n\u00a0\u00a0\u00a0\u00a0p_emp_id \u00a0 \u00a0 IN employees.emp_id%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_first_name IN employees.first_name%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_last_name\u00a0 IN employees.last_name%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_salary \u00a0 \u00a0 IN employees.salary%TYPE\r\n\u00a0\u00a0);\r\n\r\n\u00a0\u00a0PROCEDURE delete_employee(\r\n\u00a0\u00a0\u00a0\u00a0p_emp_id IN employees.emp_id%TYPE\r\n\u00a0\u00a0);\r\n\r\n\u00a0\u00a0PROCEDURE get_employee(\r\n\u00a0\u00a0\u00a0\u00a0p_emp_id \u00a0 \u00a0 IN employees.emp_id%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_first_name OUT employees.first_name%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_last_name\u00a0 OUT employees.last_name%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_salary \u00a0 \u00a0 OUT employees.salary%TYPE\r\n\u00a0\u00a0);\r\n\r\nEND emp_pkg;\r\n\/\r\n\r\nCREATE OR REPLACE PACKAGE BODY emp_pkg IS\r\n\u00a0\u00a0PROCEDURE insert_employee(\r\n\u00a0\u00a0\u00a0\u00a0p_emp_id \u00a0 \u00a0 IN employees.emp_id%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_first_name IN employees.first_name%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_last_name\u00a0 IN employees.last_name%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_salary \u00a0 \u00a0 IN employees.salary%TYPE\r\n\u00a0\u00a0) IS\r\n\r\n\u00a0\u00a0BEGIN\r\n\u00a0\u00a0\u00a0\u00a0INSERT INTO employees (emp_id, first_name, last_name, salary)\r\n\u00a0\u00a0\u00a0\u00a0VALUES (p_emp_id, p_first_name, p_last_name, p_salary);\r\n\u00a0\u00a0END insert_employee;\r\n\r\n\u00a0\u00a0PROCEDURE update_employee(\r\n\u00a0\u00a0\u00a0\u00a0p_emp_id \u00a0 \u00a0 IN employees.emp_id%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_first_name IN employees.first_name%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_last_name\u00a0 IN employees.last_name%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_salary \u00a0 \u00a0 IN employees.salary%TYPE\r\n\u00a0\u00a0) IS\r\n\r\n\u00a0\u00a0BEGIN\r\n\u00a0\u00a0\u00a0\u00a0UPDATE employees\r\n\u00a0\u00a0\u00a0\u00a0SET first_name = p_first_name,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0last_name\u00a0 = p_last_name,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0salary \u00a0 \u00a0 = p_salary\r\n\u00a0\u00a0\u00a0\u00a0WHERE emp_id = p_emp_id;\r\n\u00a0\u00a0END update_employee;\r\n\r\n\u00a0\u00a0PROCEDURE delete_employee(\r\n\u00a0\u00a0\u00a0\u00a0p_emp_id IN employees.emp_id%TYPE\r\n\u00a0\u00a0) IS\r\n\r\n\u00a0\u00a0BEGIN\r\n\u00a0\u00a0\u00a0\u00a0DELETE FROM employees\r\n\u00a0\u00a0\u00a0\u00a0WHERE emp_id = p_emp_id;\r\n\u00a0\u00a0END delete_employee;\r\n\r\n\u00a0\u00a0PROCEDURE get_employee(\r\n\u00a0\u00a0\u00a0\u00a0p_emp_id \u00a0 \u00a0 IN employees.emp_id%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_first_name OUT employees.first_name%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_last_name\u00a0 OUT employees.last_name%TYPE,\r\n\u00a0\u00a0\u00a0\u00a0p_salary \u00a0 \u00a0 OUT employees.salary%TYPE\r\n\u00a0\u00a0) IS\r\n\r\n \u00a0BEGIN\r\n\u00a0\u00a0\u00a0\u00a0SELECT first_name, last_name, salary\r\n\u00a0\u00a0\u00a0\u00a0INTO p_first_name, p_last_name, p_salary\r\n\u00a0\u00a0\u00a0\u00a0FROM employees\r\n\u00a0\u00a0\u00a0\u00a0WHERE emp_id = p_emp_id;\r\n\u00a0\u00a0END get_employee;\r\nEND emp_pkg;\r\n\/<\/pre>\n<p><span style=\"font-weight: 400;\">Translation:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false wrap:true lang:default decode:true \">.\/plsql-to-jsudf -u \u00abcapella-signin-mailid\u00bb -p \u00abcapella-signin-password\u00bb -cpaddr https:\/\/api.cloud.couchbase.com -orgid \u00abcapella-organisation-id\u00bb -cbhost \u00abhostname of data node\u00bb -cbuser \u00abcbcluster username\u00bb -cbpassword \u00abcbcluster password\u00bb\u00a0 -cbport 18093 translator\/test\/plsql\/blog_test.sql<\/pre>\n<p><span style=\"font-weight: 400;\">Code:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">function insert_employee(p_emp_id, p_first_name, p_last_name, p_salary){\r\n\u00a0\u00a0\u00a0\u00a0var params = [p_emp_id, p_first_name, p_last_name, p_salary];\r\n\u00a0\u00a0\u00a0\u00a0var query = N1QL('INSERT INTO test.testscope.employees VALUES ($1, {\"emp_id\":$1, \"first_name\":$2, \"last_name\":$3, \"salary\":$4})', params);\r\n\u00a0\u00a0\u00a0\u00a0query.close();\r\n}\r\n\r\nfunction update_employee(p_emp_id, p_first_name, p_last_name, p_salary){\r\n\u00a0\u00a0\u00a0\u00a0var params = [p_first_name, p_last_name, p_salary, p_emp_id];\r\n\u00a0\u00a0\u00a0\u00a0var query = N1QL('UPDATE test.testscope.employees SET first_name = $1, last_name = $2, salary = $3 WHERE emp_id = $4', params);\r\n\u00a0\u00a0\u00a0\u00a0query.close();\r\n}\r\n\r\nfunction delete_employee(p_emp_id){\r\n\u00a0\u00a0\u00a0\u00a0var querybegin=BEGIN WORK;\u00a0\r\n\u00a0\u00a0\u00a0\u00a0var params = [p_emp_id];\r\n\u00a0\u00a0\u00a0\u00a0var query= N1QL('DELETE FROM test.testscope.employees WHERE emp_id = $1',params);\u00a0\r\n\u00a0\u00a0\u00a0\u00a0query.close();\u00a0\r\n\u00a0\u00a0\u00a0\u00a0var querycommit=COMMIT WORK;\u00a0\r\n\u00a0\u00a0\u00a0\u00a0querycommit.close();\u00a0\r\n}\r\n\r\nfunction get_employee(p_emp_id){\r\n\u00a0\u00a0\u00a0\u00a0var query = N1QL('SELECT first_name, last_name, salary FROM test.testscope.employees WHERE emp_id = $1', [p_emp_id]);\r\n\u00a0\u00a0\u00a0\u00a0var rs = [];\r\n\u00a0\u00a0\u00a0\u00a0for (const row of query) {\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0rs.push(row);\u00a0\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0query.close();\r\n\u00a0\u00a0\u00a0\u00a0var p_first_name = rs[0]['first_name'];\r\n\u00a0\u00a0\u00a0\u00a0var p_last_name = rs[0]['last_name'];\r\n\u00a0\u00a0\u00a0\u00a0var p_salary = rs[0]['salary'];\r\n\u00a0\u00a0\u00a0\u00a0return {first_name: p_first_name, last_name: p_last_name, salary: p_salary};\r\n}\r\n\r\n<\/pre>\n<h4>Let\u2019s insert a new employee document<\/h4>\n<p><span style=\"font-weight: 400;\">Create employee collection:<\/span><\/p>\n<pre class=\"nums:false wrap:true lang:default decode:true\">curl -u\u00a0 Administrator:password https:\/\/127.0.0.1:8091\/pools\/default\/buckets\/test\/scopes\/testscope\/collections -d name=employees<\/pre>\n<p><span style=\"font-weight: 400;\">Insert an employee:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">curl -u\u00a0 Administrator:password https:\/\/127.0.0.1:18093\/query\/service -d 'statement=EXECUTE FUNCTION insert_employee(1, \"joe\", \"briggs\", 10000)' -k\u00a0\u00a0\u00a0\u00a0\u00a0\r\n{\r\n\"requestID\": \"2c0854c1-d221-42e9-af47-b6aa0801a46c\",\r\n\"signature\": null,\r\n\"results\": [\r\n],\r\n\"errors\": [{\"code\":10109,\"msg\":\"Error executing function 'insert_employee' (blog_test:insert_employee)\",\"reason\":{\"details\":{\"Code\":\"\u00a0 \u00a0 var query = N1QL('INSERT INTO test.testscope.employees VALUES ($1, {\\\"emp_id\\\":$1, \\\"first_name\\\":$2, \\\"last_name\\\":$3, \\\"salary\\\":$4})', params);\",\"Exception\":{\"_level\":\"exception\",\"caller\":\"insert_send:207\",\"code\":5070,\"key\":\"execution.insert_key_type_error\",\"message\":\"Cannot INSERT non-string key 1 of type value.intValue.\"},\"Location\":\"functions\/blog_test.js:5\",\"Stack\":\" \u00a0 at insert_employee (functions\/blog_test.js:5:17)\"},\"type\":\"Exceptions from JS code\"}}],\r\n\"status\": \"fatal\",\r\n\"metrics\": {\"elapsedTime\": \"104.172666ms\",\"executionTime\": \"104.040291ms\",\"resultCount\": 0,\"resultSize\": 0,\"serviceLoad\": 2,\"errorCount\": 1}\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">This errors out, and <\/span><b>that\u2019s ok<\/b><span style=\"font-weight: 400;\"> we can fix it manually.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Reading the reason and <em>exception: <\/em><\/span><i><span style=\"font-weight: 400;\"><em>Cannot INSERT non-string key 1 of type value.intValue<\/em>, <\/span><\/i><span style=\"font-weight: 400;\">Ah! the key is always expected to be a string, passing <\/span><b>insert_employee(&#8220;1&#8221;, &#8220;joe&#8221;, &#8220;briggs&#8221;, 10000) <\/b><span style=\"font-weight: 400;\">would do the trick, but it is unintuitive to expect <em>employee_id<\/em> to be a string.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">Let\u2019s alter the generated code:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">function insert_employee(p_emp_id, p_first_name, p_last_name, p_salary){\r\n\u00a0\u00a0\u00a0\u00a0var params = [p_emp_id.toString(), p_emp_id, p_first_name, p_last_name, p_salary];\r\n\u00a0\u00a0\u00a0\u00a0var query = N1QL('INSERT INTO test.testscope.employees VALUES ($1, {\"emp_id\":$2, \"first_name\":$3, \"last_name\":$4, \"salary\":$5})', params);\r\n\u00a0\u00a0\u00a0\u00a0query.close();\r\n}\r\n<\/pre>\n<p><b><\/b><span style=\"font-weight: 400;\">And recreate the UDF:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">curl -u\u00a0 Administrator:password https:\/\/127.0.0.1:18093\/query\/service -d 'statement=CREATE OR REPLACE FUNCTION insert_employee(p_emp_id, p_first_name, p_last_name, p_salary) LANGUAGE JAVASCRIPT AS \"insert_employee\" AT \"blog_test\"' -k\u00a0\r\n{\r\n\"requestID\": \"89df65ac-2026-4f42-8839-b1ce7f0ea2be\",\r\n\"signature\": null,\r\n\"results\": [\r\n],\r\n\"status\": \"success\",\r\n\"metrics\": {\"elapsedTime\": \"27.730875ms\",\"executionTime\": \"27.620083ms\",\"resultCount\": 0,\"resultSize\": 0,\"serviceLoad\": 2}\r\n}<\/pre>\n<p><b><\/b><b>Trying to insert again:<\/b><\/p>\n<pre class=\"nums:false lang:js decode:true\">curl -u\u00a0 Administrator:password https:\/\/127.0.0.1:18093\/query\/service -d 'statement=EXECUTE FUNCTION insert_employee(1, \"joe\", \"briggs\", 10000)' -k\r\n{\r\n\"requestID\": \"41fb76bf-a87f-4472-b8ba-1949789ae74b\",\r\n\"signature\": null,\r\n\"results\": [\r\nnull\r\n],\r\n\"status\": \"success\",\r\n\"metrics\": {\"elapsedTime\": \"62.431667ms\",\"executionTime\": \"62.311583ms\",\"resultCount\": 1,\"resultSize\": 4,\"serviceLoad\": 2}\r\n}\r\n<\/pre>\n<h4><span style=\"font-weight: 400;\">Update an employee:<\/span><\/h4>\n<p><span style=\"font-weight: 400;\">Shoot! There\u2019s a goof up, employee 1 isn\u2019t Joe, it\u2019s Emily.\u00a0<\/span><\/p>\n<p><b>Let\u2019s update employee 1<\/b><b><\/b><\/p>\n<pre class=\"nums:false lang:default decode:true\">curl -u\u00a0 Administrator:password https:\/\/127.0.0.1:18093\/query\/service -d 'statement=EXECUTE FUNCTION update_employee(1, \"Emily\", \"Alvarez\", 10000)' -k\u00a0\r\n{\r\n\"requestID\": \"92a0ca70-6d0d-4eb1-bf8d-0b4294ae987d\",\r\n\"signature\": null,\r\n\"results\": [\r\nnull\r\n],\r\n\"status\": \"success\",\r\n\"metrics\": {\"elapsedTime\": \"100.967708ms\",\"executionTime\": \"100.225333ms\",\"resultCount\": 1,\"resultSize\": 4,\"serviceLoad\": 2}\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">View the employee:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">curl -u\u00a0 Administrator:password https:\/\/127.0.0.1:18093\/query\/service -d 'statement=EXECUTE FUNCTION get_employee(1)' -k\u00a0\r\n\r\n{\r\n\"requestID\": \"8f180e27-0028-4653-92e0-606c80d5dabb\",\r\n\"signature\": null,\r\n\"results\": [\r\n{\"first_name\":\"Emily\",\"last_name\":\"Alvarez\",\"salary\":10000}\r\n],\r\n\"status\": \"success\",\r\n\"metrics\": {\"elapsedTime\": \"101.995584ms\",\"executionTime\": \"101.879ms\",\"resultCount\": 1,\"resultSize\": 59,\"serviceLoad\": 2}\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">Delete the employee:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Emily left.<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">curl -u\u00a0 Administrator:password https:\/\/127.0.0.1:18093\/query\/service -d 'statement=EXECUTE FUNCTION delete_employee(1)' -k\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\n\r\n{\r\n\"requestID\": \"18539991-3d97-40e2-bde3-6959200791b1\",\r\n\"signature\": null,\r\n\"results\": [\r\n],\r\n\"errors\": [{\"code\":10109,\"msg\":\"Error executing function 'delete_employee' (blog_test:delete_employee)\",\"reason\":{\"details\":{\"Code\":\"\u00a0 \u00a0 var querycommit=N1QL('COMMIT WORK;', {}, false); \",\"Exception\":{\"_level\":\"exception\",\"caller\":\"txcouchbase:240\",\"cause\":{\"cause\":{\"bucket\":\"test\",\"collection\":\"_default\",\"document_key\":\"_txn:atr-988-#1b0\",\"error_description\":\"Durability requirements are impossible to achieve\",\"error_name\":\"DurabilityImpossible\",\"last_connection_id\":\"eda95f8c35df6746\/d275e8398a49e515\",\"last_dispatched_from\":\"127.0.0.1:50069\",\"last_dispatched_to\":\"127.0.0.1:11210\",\"msg\":\"durability impossible\",\"opaque\":7,\"scope\":\"_default\",\"status_code\":161},\"raise\":\"failed\",\"retry\":false,\"rollback\":false},\"code\":17007,\"key\":\"transaction.statement.commit\",\"message\":\"Commit Transaction statement error\"},\"Location\":\"functions\/blog_test.js:29\",\"Stack\":\" \u00a0 at delete_employee (functions\/blog_test.js:29:21)\"},\"type\":\"Exceptions from JS code\"}}],\r\n\"status\": \"fatal\",\r\n\"metrics\": {\"elapsedTime\": \"129.02975ms\",\"executionTime\": \"128.724ms\",\"resultCount\": 0,\"resultSize\": 0,\"serviceLoad\": 2,\"errorCount\": 1}\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">Again an error with the generated code, looking at the reason and exception we can confirm that translated code encloses delete in a transaction, which wasn\u2019t the case in the original. <\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">For transactions, buckets need to have <a href=\"https:\/\/docs.couchbase.com\/server\/current\/learn\/data\/durability.html\">durability<\/a><\/span><span style=\"font-weight: 400;\">\u00a0set, but this requires more than one data server, hence the error.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">The fix here is to alter the code to remove the enclosing translation:<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">function delete_employee(p_emp_id){\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0\u00a0\u00a0var params = [p_emp_id];\r\n\u00a0\u00a0\u00a0\u00a0var query= N1QL('DELETE FROM test.testscope.employees WHERE emp_id = $1',params);\u00a0\r\n\u00a0\u00a0\u00a0\u00a0query.close();\u00a0\r\n}\r\n<\/pre>\n<pre class=\"nums:false lang:default decode:true\">curl -u\u00a0 Administrator:password https:\/\/127.0.0.1:18093\/query\/service -d 'statement=CREATE OR REPLACE FUNCTION delete_employee(p_emp_id) LANGUAGE JAVASCRIPT AS \"delete_employee\" AT \"blog_test\"' -k\u00a0\r\n\r\n{\r\n\"requestID\": \"e7432b82-1af8-4dc4-ad94-c34acea59334\",\r\n\"signature\": null,\r\n\"results\": [\r\n],\r\n\"status\": \"success\",\r\n\"metrics\": {\"elapsedTime\": \"31.129459ms\",\"executionTime\": \"31.022ms\",\"resultCount\": 0,\"resultSize\": 0,\"serviceLoad\": 2}\r\n}\r\n<\/pre>\n<pre class=\"nums:false lang:default decode:true\">curl -u\u00a0 Administrator:password https:\/\/127.0.0.1:18093\/query\/service -d 'statement=EXECUTE FUNCTION delete_employee(1)' -k\r\n\r\n{\r\n\"requestID\": \"d440913f-58ff-4815-b671-1a72b75bb7eb\",\r\n\"signature\": null,\r\n\"results\": [\r\nnull\r\n],\r\n\"status\": \"success\",\r\n\"metrics\": {\"elapsedTime\": \"33.8885ms\",\"executionTime\": \"33.819042ms\",\"resultCount\": 1,\"resultSize\": 4,\"serviceLoad\": 2}\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">Now, all functions in the original PL\/SQL work in Couchbase via JavaScript UDFs. Yes, the example is pretty trivial but you get the gist on how to go about using the tool to migrate your PL\/SQL scripts with little manual supervision. <\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">Remember the tool is supposed to take you 80% , the other 20% still needs to be done by you, but much better than writing all of that code yourself!<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">The future<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">This project is open source, so feel free to contribute. <\/span><span style=\"font-weight: 400;\">Some ideas that have been suggested:<\/span><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Critic AI that can criticize generated code to ensure manual intervention is not needed at all<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Currently the source code is code that just works; no thoughts for parallelism or code reuse were put to use.<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">And also include the limitations discussed earlier.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Resources<\/span><\/h2>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><span style=\"font-weight: 400;\"><a href=\"https:\/\/github.com\/couchbaselabs\/plsql-to-jsudf2\/releases\/tag\/v1.0.0\">Couchbase Labs GitHub<\/a> &#8211; PL\/SQL to JSUDF<\/span><\/li>\n<li><span style=\"font-weight: 400;\"><a href=\"https:\/\/www.antlr.org\/\">ANTLR<\/a> parser generator<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Finally, I\u2019d like to thank Kamini Jagtiani for guiding me and <\/span><span style=\"font-weight: 400;\">Pierre Regazzoni<\/span><span style=\"font-weight: 400;\"> for helping me test the conversion tool.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>What is PL\/SQL? PL\/SQL is a procedural language designed specifically to embrace SQL statements within its syntax. It includes procedural language elements such as conditions and loops, and can handle exceptions (run-time errors). PL\/SQL is native to Oracle databases, and [&hellip;]<\/p>\n","protected":false},"author":84423,"featured_media":16851,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1815,2225,10133,9973,9327,1812],"tags":[10043,10090,9870,1336,1592,10089,8911],"ppma_author":[9835],"class_list":["post-16846","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-best-practices-and-tutorials","category-cloud","category-engineering","category-generative-ai-genai","category-javascript","category-n1ql-query","tag-developer-tools","tag-javascript-udf","tag-llms","tag-mysql","tag-oracle","tag-pl-sql","tag-udf"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.0 (Yoast SEO v26.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF - The Couchbase Blog<\/title>\n<meta name=\"description\" content=\"Convert PL\/SQL to JavaScript UDFs seamlessly with an AI-powered tool. Automate Oracle PL\/SQL migration to Couchbase with high accuracy using ANTLR and LLMs.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF\" \/>\n<meta property=\"og:description\" content=\"Convert PL\/SQL to JavaScript UDFs seamlessly with an AI-powered tool. Automate Oracle PL\/SQL migration to Couchbase with high accuracy using ANTLR and LLMs.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2025-02-11T16:52:20+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-07-08T16:15:37+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf-1024x536.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"536\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Gaurav Jayaraj - Software Engineer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Gaurav Jayaraj - Software Engineer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/\"},\"author\":{\"name\":\"Gaurav Jayaraj - Software Engineer\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/546cec92f77cbb0b09f9b973fd1c8d42\"},\"headline\":\"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF\",\"datePublished\":\"2025-02-11T16:52:20+00:00\",\"dateModified\":\"2025-07-08T16:15:37+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/\"},\"wordCount\":1605,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png\",\"keywords\":[\"developer tools\",\"javascript UDF\",\"LLMs\",\"mysql\",\"oracle\",\"pl\/sql\",\"User Defined Function (UDF)\"],\"articleSection\":[\"Best Practices and Tutorials\",\"Couchbase Capella\",\"Engineering\",\"Generative AI (GenAI)\",\"JavaScript\",\"SQL++ \/ N1QL Query\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/\",\"name\":\"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF - The Couchbase Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png\",\"datePublished\":\"2025-02-11T16:52:20+00:00\",\"dateModified\":\"2025-07-08T16:15:37+00:00\",\"description\":\"Convert PL\/SQL to JavaScript UDFs seamlessly with an AI-powered tool. Automate Oracle PL\/SQL migration to Couchbase with high accuracy using ANTLR and LLMs.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png\",\"width\":2400,\"height\":1256,\"caption\":\"A tool for converting PL\/SQL to JavaScript UDF (JSUDF) using LLMs\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/546cec92f77cbb0b09f9b973fd1c8d42\",\"name\":\"Gaurav Jayaraj - Software Engineer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/a332e5d7f47865015367ca88af3e5891\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg\",\"caption\":\"Gaurav Jayaraj - Software Engineer\"},\"description\":\"Gaurav Jayaraj is an intern in the Query team at Couchbase R&amp;D. Gaurav is pursuing his Bachelors in Computer Science from PES University, Bangalore.\",\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/gauravjayaraj\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF - The Couchbase Blog","description":"Convert PL\/SQL to JavaScript UDFs seamlessly with an AI-powered tool. Automate Oracle PL\/SQL migration to Couchbase with high accuracy using ANTLR and LLMs.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/","og_locale":"en_US","og_type":"article","og_title":"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF","og_description":"Convert PL\/SQL to JavaScript UDFs seamlessly with an AI-powered tool. Automate Oracle PL\/SQL migration to Couchbase with high accuracy using ANTLR and LLMs.","og_url":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/","og_site_name":"The Couchbase Blog","article_published_time":"2025-02-11T16:52:20+00:00","article_modified_time":"2025-07-08T16:15:37+00:00","og_image":[{"width":1024,"height":536,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf-1024x536.png","type":"image\/png"}],"author":"Gaurav Jayaraj - Software Engineer","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Gaurav Jayaraj - Software Engineer","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/"},"author":{"name":"Gaurav Jayaraj - Software Engineer","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/546cec92f77cbb0b09f9b973fd1c8d42"},"headline":"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF","datePublished":"2025-02-11T16:52:20+00:00","dateModified":"2025-07-08T16:15:37+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/"},"wordCount":1605,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png","keywords":["developer tools","javascript UDF","LLMs","mysql","oracle","pl\/sql","User Defined Function (UDF)"],"articleSection":["Best Practices and Tutorials","Couchbase Capella","Engineering","Generative AI (GenAI)","JavaScript","SQL++ \/ N1QL Query"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/","url":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/","name":"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF - The Couchbase Blog","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png","datePublished":"2025-02-11T16:52:20+00:00","dateModified":"2025-07-08T16:15:37+00:00","description":"Convert PL\/SQL to JavaScript UDFs seamlessly with an AI-powered tool. Automate Oracle PL\/SQL migration to Couchbase with high accuracy using ANTLR and LLMs.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png","width":2400,"height":1256,"caption":"A tool for converting PL\/SQL to JavaScript UDF (JSUDF) using LLMs"},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"The Couchbase Blog","description":"Couchbase, the NoSQL Database","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/546cec92f77cbb0b09f9b973fd1c8d42","name":"Gaurav Jayaraj - Software Engineer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/a332e5d7f47865015367ca88af3e5891","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg","caption":"Gaurav Jayaraj - Software Engineer"},"description":"Gaurav Jayaraj is an intern in the Query team at Couchbase R&amp;D. Gaurav is pursuing his Bachelors in Computer Science from PES University, Bangalore.","url":"https:\/\/www.couchbase.com\/blog\/author\/gauravjayaraj\/"}]}},"authors":[{"term_id":9835,"user_id":84423,"is_guest":0,"slug":"gauravjayaraj","display_name":"Gaurav Jayaraj - Software Engineer","avatar_url":{"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg","url2x":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg"},"author_category":"","last_name":"Jayaraj - Software Engineer","first_name":"Gaurav","job_title":"","user_url":"","description":"Gaurav Jayaraj is an intern in the Query team at Couchbase R&amp;D. Gaurav is pursuing his Bachelors in Computer Science from PES University, Bangalore."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/16846","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/84423"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=16846"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/16846\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/16851"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=16846"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=16846"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=16846"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=16846"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}