1

【oracle】关于21c版本新增plsql包dbms-利来国际app

前言

在前几年偶然间找到过一篇文章

当时这篇文章中就是用的dbms_mle包,但是oracle中当时实际上并没有这个包,这篇文章应该是相关开发者在开发这个包时的测试记录,可以看到这篇文章中,可以在plsql中使用python或者javascirpt的语法来编写过程。其原理是使用了graalvm ()

graalvm是个相当有意思的项目,它自称全栈虚拟机,可以让你跨平台使用多种开发语言,下面这张图,框框里面的就是它能跑的开发语言,框框外的就是运行它的环境,按照这张图描述的,你甚至可以在node.js里面跑python,在oracle里面跑ruby等等

这比我"aio"的想法更疯狂,我还只是想让oracle能支持更多内容,而graalvm这个项目是朝着大一统的方向走。

目前仅支持javascirpt

然后oracle在21c版本,正式在数据库中集成了graalvm,对应plsql的api就是dbms_mle,在此之前,mle只是作为apex插件存在。
另外我还搜索到老版本dbms_mle和新版本的一个官方解答
https://community.oracle.com/tech/developers/discussion/4348385/oracle-mle-is-dead

的确之前dbms_mle作为测试版,在github上进行过开源,但是原页面目前已经404了,现在只能使用21c版本的了。

然而在官方文档中只有javascirpt的示例,没有python。
而且拿早期那篇测试文章和官方示例比较,包内的函数名及过程名是有差异的,比如

  1. “script” 都被替换成了"context"
  2. “bind_variable"变成了"export_to_mle”
  3. “create_script"变成了"eval”
  4. “variable_value"变成了"import_from_mle”

但程序的基本结构还是差不多的,于是我尝试使用早期测试文中python的例子,然后替换这些名称进行测试,结果提示

ora-04101: unsupported language python
ora-06512: at “sys.dbms_mle”, line 385
ora-06512: at line 26

说是不支持python,那么它除了javascirpt以外,还支持哪些开发语言呢?

在dbms_mle包中,有一个函数get_available_languages,

function get_available_languages return languages_t is result dbms_mle.languages_t := dbms_mle.languages_t(); begin icd_mlegal; result.extend(); result(1) := 'javascript'; return result; end;

其中“icd_mlegal”是c里面的,plsql中看不了,但按照这个写法,很难不让人觉得,它目前的确就只支持javascript。

看样子要使用oracle官方提供的,在plsql中运行python代码的方式目前只有oml4py了。

调用javascirpt的语法分析

既然支持不了python ,那就来看看要怎么写代码才能让它执行javascript代码。

(国内其实有eygle大佬简单介绍了这个包,但是并没有对这个包进行分析。 https://www.modb.pro/db/43023 )

来看看官方示例

set serveroutput on; declare ctx dbms_mle.context_handle_t; source clob; greeting varchar2(100); begin ctx := dbms_mle.create_context(); -- create execution context for mle execution dbms_mle.export_to_mle(ctx, 'person', 'world'); -- export value from pl/sql source := q'~ var bindings = require("mle-js-bindings"); var person = bindings.importvalue("person"); // import value previously exported from pl/sql var greeting = "hello, " person "!"; bindings.exportvalue("greeting", greeting); // export value to pl/sql ~'; dbms_mle.eval(ctx, 'javascript', source); -- evaluate the source code snippet in the execution context dbms_mle.import_from_mle(ctx, 'greeting', greeting); -- import value previously exported from mle dbms_output.put_line('greetings from mle: ' || greeting); dbms_mle.drop_context(ctx); -- drop the execution context once no longer required end; /

1.create_context() 生成一个context_handle_t(raw(16))
2.export_to_mle,将参数传到mle里面,此例为,把参数’person’的值赋成’world’
3.定义js代码的字符串

3.1 js代码需要"mle-js-bindings"这个外部模块来定义一个对象?先继续往下看
3.2 变量person赋值,用importvalue,这个就对应上面的export_to_mle
3.3 字符串"hello, "拼接变量person
3.4 使用exportvalue导出变量greeting

4.使用eval执行这段js脚本
5.使用import_from_mle,将参数greeting从mle中导入回plsql
6.打印greeting ,是 “hello, world!”
7.最后删掉最开始生成的context_handle_t

仔细看完才发现,这个功能不是把随便写点js代码放进去就行的,就算js里面写了return值,默认情况下也不会传回到plsql。它实际上是在js环境中提前嵌入了一个"mle-js*"的库,这个库可以在运行时和oracle进行参数传递,

  1. plsql要传入js,则plsql执行export_to_mle,js执行require(“mle-js-bindings”).importvalue(“person”)
  2. js要传回plsql,则js执行require(“mle-js-bindings”).exportvalue,plsql执行import_from_mle

当然如果不进行参数传递,只进行纯粹的js执行,倒也不用管这个,直接一个eval就好了.

也就是说,常规的js函数要能和plsql进行交互,就必须改造一下js代码,使用mle的功能来进行参数的输入及输出。当然理论上也可以在外层进行引用,只是,如果要执行的代码依赖多个js文件,这个又该怎么写呢?

如何引用外部js模块

(先补充一下,js中写的"console.log();"也会出现在plsql的output池里,可以通过get_line获取内容,这点和那篇早期测试文一致,这个功能其实可以方便我们调试js代码的部分,但oracle官方的正式文档中没有提到。)

单一js代码要实现很复杂的功能会是件麻烦事,一般的js项目都会创建多个js文件,这样可以让代码更整洁,功能区分更明确,oracle官方的目的是,会js开发的程序员,也可以在plsql里写代码了,但是实际上这个功能更靠谱的场景应该是plsql程序员遇到了一个复杂功能的需求,刚好有外部开源的js程序,期望能直接在plsql里用上这个外部js程序。

突破点应该就在 require(“mle-js-bindings”) 这里,如果能在os上找到mle-js-bindings文件,那么理论上就能直接引用外部文件,

然而啥都没有,那么如果直接写绝对引用呢?

写了一小段js,保存在了“/home/oracle/test.js”,然后在上面的js代码中插入一行

var test = require("/home/oracle/test");

运行报错

ora-04103: module /home/oracle/test does not exist
ora-06512: at “sys.dbms_mle”, line 385
ora-06512: at line 16

也就是说,它其实不是在os上运行js的,回到这个篇文章的起点,“graalvm”,其实是个虚拟机,也就是说,它只能使用它内部已经有的东西。

那么,问题就变成了,怎么给这个虚拟机添加文件?
require(“path”),提示没有这个module,用import也会报错,把文件后缀改成"mjs"也不行。
还有很多js和node.js自带的一些module也没有。

翻阅了graalvm的官方网站,所有有关dbms_mle的例子中,require的只有两个,"mle-js-bindings"用于传递参数,"mle-js-oracledb"用于连接oracle数据库执行sql,完全没提到引用其他js,反倒是有说apex的mle功能更为强大和通用。
也难怪现在没几篇文章说这个东西。

https://mle-databaseraalvm/mle-executing-javascript-in-oracle-database-c545feb1a010 (有墙)
这篇文章算是说得最全的了,连架构图都来了,


但是依旧没说到怎么引用其他js文件,也没说怎么接触到graalvm在oracle中的runtime。

毕竟21c是创新者版本,估计更多的功能要等以后完善了吧.

完整版的graalvm其本身是可以导入其他js模块的,但是oracle版本的没有说怎么弄。

但既然已经研究到了这里,不拿点实际的应用出来,有人会说这个玩意不实用了吧?我自己写几个例子试试。

写点代码试试

首先来个简单的

declare ctx dbms_mle.context_handle_t; source clob; r number; begin ctx := dbms_mle.create_context(); dbms_mle.export_to_mle(ctx, 'a', 5); dbms_mle.export_to_mle(ctx, 'b', 6); source := q'~ function myfunction(p1, p2) { return p1 * p2; } var bindings = require("mle-js-bindings"); var a = bindings.importvalue("a"); var b = bindings.importvalue("b"); var r = myfunction(a, b); bindings.exportvalue("r", r); ~'; dbms_mle.eval(ctx, 'javascript', source); dbms_mle.import_from_mle(ctx, 'r', r); dbms_output.put_line('r: ' || r); dbms_mle.drop_context(ctx); end; / r: 30

/
这是个简单的js函数,证实我前面的分析没错。这段可以改成plsql的函数,那么就可以在plsql中无感使用js了。

接下来再写个更复杂的例子
在网上找到了一个生成二维码的js
https://cdnjs.cloudflare.com/ajax/libs/qrcode-generator/1.4.4/qrcode.min.js
然后封装成plsql的函数,效果如下

大概思路是用utl_http获取这个js文件的内容,然后再在后面拼接上调用它的js代码,执行这个拼接后的js,会返回一个base64字符串,然后再使用utl_encode转回成二进制数据。

因为js是可以混淆、压缩、打包的,所以,如果某个外部js模块有很多个零散的js的文件,可以用一些工具比如webpack把它做成一个js文件,然后再用。如果数据库不方便连外网,我们可以把这个js的内容保存到数据库表的clob字段中,并给它命名,以方便使用不同js模块的时候调用。

这个生成二维码的plsql函数代码我已经做成了单文件版,并已上传到github

总结

虽然dbms_mle功能目前不够完善,但的确可以使用了,未来可期。

  • 本文作者: darkathena
  • 本文链接:
  • 利来手机国际的版权声明: 本博客所有文章除特别声明外,均采用 许可协议。转载请注明出处!
最后修改时间:2021-12-30 21:27:00
「喜欢文章,快来给作者赞赏墨值吧」
【利来手机国际的版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:[email protected]进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论