为什么使用 JavaScript 存储过程?
JavaScript 是最流行的编程语言之一,除了更简单的语法和对现代语言特性的支持外,它受欢迎的一个关键因素是丰富的生态系统,它在浏览器、服务端、客户端等场景提供了大量可重用的代码模块。
在数据库中调用 JavaScript
JavaScript 可通过动态执行或调用规范来调用。调用规范可以引用 MLE 模块,也可以引用内联的 JavaScript 函数。
一般来说,服务器端的 JavaScript 代码可以通过两种方式调用:
-
通过
DBMS_MLE包进行动态调用。 -
使用 PL/SQL 代码,引用 JavaScript 模块中导出的函数(即所谓的 MLE 模块调用)或直接在 DDL 中定义的函数。
动态调用!
SQL> SET SERVEROUTPUT ON; SQL> DECLARE 2 l_ctx DBMS_MLE.context_handle_t; 3 l_jscode CLOB; 4 BEGIN 5 l_ctx := DBMS_MLE.create_context; 6 l_jscode := q'~ 7 console.log('Hello World, this is DBMS_MLE') 8 ~'; 9 DBMS_MLE.eval( 10 context_handle => l_ctx, 11 language_id => 'JAVASCRIPT', 12 source => l_jscode, 13 source_name => 'My JS Snippet' 14 ); 15 END; 16 / Hello World, this is DBMS_MLE PL/SQL procedure successfully completed SQL>
上面提供的代码演示了以下动态调用 JavaScript 代码的概念:
-
必须显式创建一个执行上下文。
-
JavaScript 代码作为字符大对象或
VARCHAR2类型变量提供。 -
必须对上下文进行显式求值。
当您动态执行 JavaScript 时,会同时涉及 PL/SQL 和 JavaScript。所提供的代码片段在其命名空间之外是不可重用的。对 console.log 的调用所产生的输出会被传递至 DBMS_OUTPUT,以便在屏幕上打印显示
MLE 模块调用简介
DBMS_MLE.eval() 函数用于计算并返回 JavaScript 代码的执行结果。
可以将 JavaScript 模块创建为模式对象,并持久化存储在数据库中。
一旦定义了 JavaScript 模块,就可以如下所示在 SQL 和 PL/SQL 中使用它。
SQL> CREATE OR REPLACE MLE MODULE helloWorld_module 2 LANGUAGE JAVASCRIPT AS 3 function helloWorld() { 4 console.log('Hello World, this is a JS module'); 5 } 6 export { helloWorld } 7 / MLE module created.
在调用已导出的 JavaScript 函数之前,必须定义一个调用规范。
下面的代码片段展示了如何在 PL/SQL 中为 JavaScript 的 helloWorld() 函数创建一个调用规范:
SQL> CREATE OR REPLACE PROCEDURE helloWorld_proc 2 AS MLE MODULE helloWorld_module 3 SIGNATURE 'helloWorld()'; 4 / Procedure created.
这个被称为 MLE 模块调用的调用规范,公开了 JavaScript 函数 helloWorld()。此后,它便可以像任何其他 PL/SQL 过程一样使用。以下代码片段展示了如何调用该函数及其结果:
SQL> SET SERVEROUTPUT ON SQL> BEGIN 2 helloWorld_proc; 3 END; 4 / Hello World, this is a JS module PL/SQL procedure successfully completed. SQL>
DBMS_MLE.EXPORT_TO_MLE 导出的符号。传递给 MLE 的引用类型(如 LOB)可以在 PURE 执行期间被访问(读取或写入)。此外,PURE 执行不限制对支持的数据类型的访问。-
JavaScript API:
-
mle-js-oracledb -
mle-js-plsql-ffi -
mle-js-fetch
-
-
全局类和函数:
-
session -
soda -
plsffi -
oracledb -
require
-
js-encodings)在 PURE 执行期间仍然可访问。SQL> CREATE OR REPLACE MLE MODULE pure_mod 2 LANGUAGE JAVASCRIPT AS 3 export function helloWorld() { 4 console.log('Hello World, this is a JS module'); 5 } 6 / MLE module created.
SQL> SQL> CREATE OR REPLACE MLE ENV pure_env 2 IMPORTS( 'pure_mod' MODULE pure_mod) PURE; MLE env created. SQL> SQL> CREATE OR REPLACE PROCEDURE helloWorld 2 AS MLE MODULE pure_mod ENV pure_env SIGNATURE 'helloWorld'; 3 / Procedure created. SQL> CREATE OR REPLACE PROCEDURE helloWorld 2 AS MLE LANGUAGE JAVASCRIPT PURE 3 {{ 4 console.log('Hello World, this is a JS inlined call specification'); 5 }}; 6 / Procedure created.
SQL> set serveroutput on
SQL> exec helloWorld
Hello World, this is a JS inlined call specification
PL/SQL procedure successfully completed.
SQL>
SQL> SET SERVEROUTPUT ON; SQL> DECLARE 2 l_ctx dbms_mle.context_handle_t; 3 l_snippet CLOB; 4 BEGIN 5 -- to specify PURE execution with DBMS_MLE, use an environment 6 -- that has been created with the PURE keyword 7 l_ctx := dbms_mle.create_context(environment => 'PURE_ENV'); 8 l_snippet := q'~ 9 console.log('Hello World, this is dynamic MLE execution'); 10 ~'; 11 dbms_mle.eval(l_ctx, 'JAVASCRIPT', l_snippet); 12 dbms_mle.drop_context(l_ctx); 13 EXCEPTION 14 WHEN OTHERS THEN 15 dbms_mle.drop_context(l_ctx); 16 RAISE; 17 END; 18 / Hello World, this is dynamic MLE execution PL/SQL procedure successfully completed.
mle-js-oracledb,访问数据库:SQL> -- 创建调试版本 SQL> CREATE OR REPLACE MLE MODULE debug_module 2 LANGUAGE JAVASCRIPT AS 3 4 import oracledb from "mle-js-oracledb"; 5 6 export function debugCount() { 7 const conn = oracledb.defaultConnection(); 8 9 // 分步调试 10 console.log("步骤1: 测试连接"); 11 const test = conn.execute(`SELECT 'TEST' as status FROM dual`); 12 console.log("测试查询结果: " + JSON.stringify(test.rows[0])); 13 14 console.log("步骤2: 执行统计查询"); 15 const result = conn.execute(` 16 SELECT COUNT(*) as row_count 17 FROM user_tables 18 WHERE table_name = 'TAB_TEST1' 19 `); 20 console.log("检查表是否存在: " + JSON.stringify(result.rows[0])); 21 22 console.log("步骤3: 统计行数"); 23 const countResult = conn.execute(` 24 SELECT COUNT(*) as total_rows 25 FROM tab_test1 26 `); 27 28 console.log("统计结果: " + JSON.stringify(countResult)); 29 30 // 直接返回值 31 return countResult.rows[0][0]; 32 } 33 / CREATE OR REPLACE FUNCTION debug_tab_count MLE module created. SQL> SQL> 2 RETURN NUMBER 3 AS MLE MODULE debug_module 4 SIGNATURE 'debugCount'; 5 / Function created. SQL> SQL> -- 执行调试 SQL> SELECT debug_tab_count() FROM DUAL; DEBUG_TAB_COUNT() ----------------- 步骤1: 测试连接 测试查询结果: {"STATUS":"TEST"} 步骤2: 执行统计查询 检查表是否存在: {"ROW_COUNT":1} 步骤3: 统计行数 统计结果: {"metaData":[{"name":"TOTAL_ROWS"}],"rows":[{"TOTAL_ROWS":3}]} SQL> SELECT debug_tab_count() FROM DUAL; DEBUG_TAB_COUNT() ----------------- 步骤1: 测试连接 测试查询结果: {"STATUS":"TEST"} 步骤2: 执行统计查询 检查表是否存在: {"ROW_COUNT":1} 步骤3: 统计行数 统计结果: {"metaData":[{"name":"TOTAL_ROWS"}],"rows":[{"TOTAL_ROWS":3}]} SQL>
关键PDB,CDB参数控制
SQL> alter system set mle_prog_languages=off; System altered. SQL> SELECT debug_tab_count() FROM DUAL; SELECT debug_tab_count() FROM DUAL * ERROR at line 1: ORA-00439: feature not enabled: MLE Help: https://docs.oracle.com/error-help/db/ora-00439/ SQL> alter system set mle_prog_languages=all; System altered. SQL> SELECT debug_tab_count() FROM DUAL; DEBUG_TAB_COUNT() ----------------- 步骤1: 测试连接 测试查询结果: {"STATUS":"TEST"} 步骤2: 执行统计查询 检查表是否存在: {"ROW_COUNT":1} 步骤3: 统计行数 统计结果: {"metaData":[{"name":"TOTAL_ROWS"}],"rows":[{"TOTAL_ROWS":3}]} SQL>
https://docs.oracle.com/en/database/oracle/oracle-database/26/nfcoa/oracle-ai-database-26ai-new-features-guide.pdf
https://docs.oracle.com/en/database/oracle/oracle-database/26/mlejs/mle-type-conversions.html#GUID-E2E58A1A-DE51-40CE-8672-2AD60EEAB008
https://docs.oracle.com/en/database/oracle/oracle-database/26/mlejs/soda-collections-in-mle-js.html
学习大纲

浙公网安备 33010602011771号