2024年5月28日火曜日

Oracle Database 23aiのJavaScriptでのモジュールの呼び出し方を調べる

Oracle Database 23aiのMLE - Multilingual Engineで、JavaScriptのモジュールを呼び出す方法について確認してみました。

以下の資料を主に参照しています。

Oracle Database Release 23
JavaScript Developer's Guide
4 Overview of Importing MLE JavaScript Modules

実装のサンプルとしては、以下のLiveLabsも参考にしています。
APEX + Server-Side JavaScript (MLE)
Lab 4: Using External Modules

JavaScriptの実行には、Always FreeのOracle Autonomous Database 23aiのOracle APEX 23.2を使います。

Example 4-2のFunction Export using Named Exportsに記載されているMLEモジュールnamed_exports_moduleを作成します。モジュールに含まれるファンクションsumとdifferenceがエクスポートされます。
CREATE OR REPLACE MLE MODULE named_exports_module LANGUAGE JAVASCRIPT AS

function sum(a, b) {
    return a + b;
}

function difference(a, b) {
    return a - b;
}

export {sum, difference};
/
SQLワークショップオブジェクト・ブラウザより、MLEモジュールを作成します。


名前NAMED_EXPORTS_MODULEです。APEXからMLEモジュールを作成する際は名前が大文字になりますが、コードで作成してもダブル・クォーテーションで囲まなければ、ディクショナリには大文字で登録されます。

ソース・タイプソース・コードを選択し、ソース・コード(JAVASCRIPT AS 以下)を記述します。

MLEモジュールの作成をクリックします。


MLEモジュールとしてNAMED_EXPORTS_MODULEが作成されます。


作成したMLEモジュールに含まれる、エクスポートされたファンクションsumおよびdiffrenceを呼び出してみます。

MLE環境としてNAMED_EXPORTS_ENVを作成します。
CREATE MLE ENV named_exports_env;

環境名NAMED_EXPORTS_ENVとします。

MLE環境の作成をクリックします。


MLE環境としてNAMED_EXPORTS_ENVが作成されます。インポート・タブを開き、インポートの追加をクリックします。

先ほど作成したMLEモジュールNAMED_EXPORTS_MODULEを、このMLE環境NAMED_EXPORTS_ENVにインポート可能なモジュールとして登録します。


内部的には以下のスクリプトが実行されます。
alter MLE ENV named_exports_env
    add imports('namedExports' module named_exports_module);
モジュール所有者APEXのワークスペース・スキーマモジュール名NAMED_EXPORTS_MODULEです。インポート名namedExportsとします。インポート名は大文字小文字が区別されます。

作成をクリックします。


MLE環境NAMED_EXPORTS_ENVにインポート可能なMLEモジュールとして、NAMED_EXPORTS_MODULEが追加されます。JavaScriptのコードから、このモジュールをインポートする際には、インポート名namedExportsを指定します。


SQLワークショップSQLコマンドから、モジュールNAMED_EXPORTS_MODULEでエクスポートされているファンクションsumdifferenceを呼び出してみます。

言語としてJavaScript(MLE)を選択し、環境としてNAMED_EXPORTS_ENVを選択します。MLEモジュールnamedExportsのインポートは、以下の文で実行します。

const { sum, difference } = await import("namedExports");

以下のスクリプトを実行します。
const { sum, difference } = await import("namedExports");
console.log(sum(2,1));
console.log(difference(2,1));
sumとdifferenceが呼び出せます。


以下のスクリプトでも確認します。

const e = await import("namedExports");

結果は同じになります。
const e = await import("namedExports");
console.log(e.sum(2,1));
console.log(e.difference(2,1));

MLEモジュールNAMED_EXPORTS_MODULEsumdifferenceを呼び出すために、このモジュールをインポートするMLEモジュールNAMED_IMPORTS_MODULEを作成します。

Example 4-7 Named Imports Using Specified Identifiersとして記載されているコードを実行します。
CREATE OR REPLACE MLE MODULE named_imports_module
LANGUAGE JAVASCRIPT AS

import {sum, difference} from "namedExports";

function mySum(){
    const result = sum(4, 2);
    console.log(`the sum of 4 and 2 is ${result}`);
}

function myDifference(){
    const result = difference(4, 2);
    console.log(`the difference between 4 and 2 is ${result}`);
}

export {mySum, myDifference};
/
先ほどと同じ手順でMLEモジュールの作成を呼び出します。

名前NAMED_IMPORTS_MODULEソース・タイプソース・コードです。MLEモジュールNAMED_EXPORTS_MODULEのインポートは、以下の文で実行します。

import {sum, difference} from "namedExports";


作成したMLEモジュールをMLE環境NAMED_EXPORTS_ENVのインポートに追加します。インポート名namedImportsとします。


MLE環境NAMED_EXPORTS_ENVにインポート名namedImportsが追加されます。


SQLコマンドよりインポート名namedImportsを指定してインポートし、ファンクションmySummyDifferenceを呼び出してみます。
const { mySum, myDifference } = await import("namedImports");
mySum();
myDifference();
namedImportsとしてインポートされたMLEモジュールNAMED_IMPORTS_MODULEから、MLEモジュールNAMED_EXPORTS_MODULEのファンクションが参照できていることが確認できます。


Example 4-6 Module Object Definitionに記載されているモジュール本文は以下です。この内容でモジュールNAMED_IMPORTS_MODULEを置き換えても、動作は変わりません。
import * as myMath from "namedExports"

function mySum(){
    const result = myMath.sum(4, 2);
    console.log(`the sum of 4 and 2 is ${result}`);
}

function myDifference(){
    const result = myMath.difference(4, 2);
    console.log(`the difference between 4 and 2 is ${result}`);
}

export {mySum, myDifference};
Example 4-8 Named Imports with Aliasesの本文も同様です。
import {sum as theSum, difference as theDifference} from "namedExports";

function mySum(){
    const result = theSum(4, 2);
    console.log(`the sum of 4 and 2 is ${result}`);
}

function myDifference(){
    const result = theDifference(4, 2);
    console.log(`the difference between 4 and 2 is ${result}`);
}

export {mySum, myDifference};
実際にモジュールNAMED_IMPORTS_MODULEの本文を置き変え、保存してコンパイルを行います


コンパイル後にオブジェクト・ブラウザをリロードすると、MLE環境NAMED_EXPORTS_ENVが無効になっていることが確認できます。


現在のところ、オブジェクト・ブラウザにはMLE環境をバリッドに戻す方法が提供されていません。

SQLコマンドを開き、言語SQLにして以下のALTER文を実行し、MLE環境をコンパイルします。

alter mle env named_exports_env compile;


今までは簡単なコードを書いてMLEモジュールを作成していました。

実際はESモジュールからMLEモジュールを作成することができます。

文字列の検証を行うvalidator.jsをMLEモジュールとして作成してみます。MLEモジュールのソースとして以下のURLを指定します。

https://cdn.jsdelivr.net/npm/validator@13.12.0/+esm

MLEモジュールの作成画面を開きます。

名前VALIDATORソース・タイプとしてURLを選択し、URLにESモジュールのソースを指すURLを指定します。


作成したMLEモジュールVALIDATORをMLE環境NAMED_EXPORTS_ENVインポート名validatorとして追加します。


SQLコマンドから、validator.jsに含まれているファンクションisEmailを呼び出してみます。

以下のコードを実行します。
const validator = await import("validator");
console.log(validator.default.isEmail("yuji"));
console.log(validator.default.isEmail("yuji@oracle.com"));

isEmailの最初の呼び出しはfalse、次の呼び出しはtrueが返されることが確認できます。


Oracle DatabaseのMLEではモジュールがDatabaseに保存されているため、Node.jsやブラウザとは扱い方が若干異なります。そのため、Oracle Database 23aiのMLE上でJavaScriptを実行するにあたって、モジュールがどのように扱われるかを確認してみました。

ちなみにOracle APEXのアプリケーションで参照するMLE環境は、アプリケーション定義セキュリティMLE環境で設定します。


今回の記事は以上になります。