大势趋007

每个人都是🏆 时时刻刻::::: 1 新的技术经常性尝试,经常性测试;;;;; 2 一线dba的经常性操作和总结,二线技术的经常性思考
  新随笔  :: 管理

oracle javascripts 学习

Posted on 2026-02-02 16:08  大势趋007  阅读(1)  评论(0)    收藏  举报

为什么使用 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> 

 

关于受限执行环境(PURE 执行)
PURE 关键字可用于 MLE 环境和 JavaScript 内联调用规范,以创建受限的 JavaScript 执行上下文。
数据库内的 JavaScript 代码可以利用 MLE JavaScript SQL 驱动和 SODA 等 API 来执行数据库功能。而 PURE 执行禁止在 JavaScript 中访问有状态的数据库 API,这意味着执行是完全无特权的。
在 PURE 环境中,JavaScript 代码无法读取或写入任何数据库状态(如表、过程和对象)。PURE 执行期间与数据库的唯一可能交互是通过 JavaScript 代码的输入和输出。这可以是通过用户定义函数参数从数据库提供给 MLE 的数据,以及使用 DBMS_MLE.EXPORT_TO_MLE 导出的符号。传递给 MLE 的引用类型(如 LOB)可以在 PURE 执行期间被访问(读取或写入)。此外,PURE 执行不限制对支持的数据类型的访问。
在许多情况下,JavaScript 用户定义函数纯粹是计算性的,不需要访问 MLE JavaScript SQL 驱动或外部函数接口(FFI)等强大的 API。PURE 执行可作为将某些代码(如第三方 JavaScript 库)与数据库本身隔离的方法。这种隔离可以减少供应链攻击的攻击面,因为在供应链攻击中,访问数据库状态是一个安全问题。
使用 PURE 执行还允许权限较低的开发者创建这些受限的用户定义函数,而无需额外访问数据库状态或网络的权限。
以下 JavaScript API、全局类和函数在 PURE 执行期间不可用:
  • JavaScript API:
    • mle-js-oracledb
    • mle-js-plsql-ffi
    • mle-js-fetch
  • 全局类和函数:
    • session
    • soda
    • plsffi
    • oracledb
    • require
不与数据库状态交互的 JavaScript API(如 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

 学习大纲

NotebookLM Mind Map