在 Oracle 資料庫中使用 JavaScript
此頁面說明如何使用 Oracle 資料庫多語言引擎 (MLE) 在 Oracle 資料庫中執行 JavaScript。MLE 由 GraalVM 提供支援:它可以在 Linux x64 上的 Oracle 資料庫 23ai (及更高版本) 中執行 JavaScript 程式碼。
準備工作
- 取得 Oracle Cloud 免費層帳戶,並選擇提供 Oracle 資料庫 23ai 或更高版本的首選區域。
- 佈建 Autonomous Database Transaction Processing 執行個體 並啟動您最愛的 SQL IDE,例如以下其中一種:
必要條件:請參閱在 Oracle 資料庫 23c 免費開發人員版本中介紹 JavaScript 中標題為「在 Oracle 資料庫 23c 免費開發人員版本中使用 JavaScript 的必要條件」的章節。(雖然目標是較早的版本,但它們仍然有效。)
範例
這些範例假設您已建立一個具有必要權限的資料庫帳戶,可以建立 MLE 模組和環境,以及 PL/SQL 函式、程序和套件。該帳戶也已獲授予執行 JavaScript 程式碼的權限。(如需更多資訊,請參閱MLE 安全性。)
-
第一個範例示範如何建立 MLE 模組。
create or replace mle module helper_module_inline language javascript as /** * Convert a delimited string into key-value pairs and return JSON * @param {string} inputString - the input string to be converted * @returns {JSON} */ function string2obj(inputString) { if ( inputString === undefined ) { throw `must provide a string in the form of key1=value1;...;keyN=valueN`; } let myObject = {}; if ( inputString.length === 0 ) { return myObject; } const kvPairs = inputString.split(";"); kvPairs.forEach( pair => { const tuple = pair.split("="); if ( tuple.length === 1 ) { tuple[1] = false; } else if ( tuple.length != 2 ) { throw "parse error: you need to use exactly one '=' between " + "key and value and not use '=' in either key or value"; } myObject[tuple[0]] = tuple[1]; }); return myObject; } /** * Convert a JavaScript object to a string * @param {object} inputObject - the object to transform to a string * @returns {string} */ function obj2String(inputObject) { if ( typeof inputObject != 'object' ) { throw "inputObject isn't an object"; } return JSON.stringify(inputObject); } export { string2obj, obj2String } /
注意:MLE 僅支援 ECMA Script 模組。CommonJS 和其他常用技術無法使用。
上述模組定義了兩個函式
string2obj()
obj2Str()
模組結尾的
export
關鍵字表示其他 MLE 模組、PL/SQL 以及 SQL 可以呼叫這些函式。 -
第二個範例示範如何讓這些函式在 SQL 和 PL/SQL 中可用。JavaScript 模組中的函式無法在 SQL 和 PL/SQL 中直接呼叫。您需要為它們建立呼叫規格。呼叫規格可以被視為 PL/SQL 中的「包裝函式」。根據您的需求,您可以使用獨立的函式和程序,或將它們分組在套件中。由於這兩個函式屬於
helper_module
,因此它們會放在一個套件中。create or replace package helper_pkg as function string2obj( p_inputString varchar2 ) return JSON as mle module helper_module_inline signature 'string2obj'; function obj2String( p_inputObject JSON ) return varchar2 as mle module helper_module_inline signature 'obj2String'; end helper_pkg; /
在完成呼叫規格後,您可以將以
;
分隔的字串轉換為 JavaScript 物件,並將其返回給呼叫方,如下所示select json_serialize( helper_pkg.string2obj( 'order_id=1;order_date=2023-04-24T10:27:52;order_mode=mail;promotion_id=1' ) pretty ) string2obj;
您應該會看到以下輸出
STRING2OBJ ---------------------------------------- { "order_id" : "1", "order_date" : "2023-04-24T10:27:52", "order_mode" : "mail", "promotion_id" : "1" }
-
從 Oracle 資料庫 23.3 開始,如果您只需要呼叫標準 JavaScript 程式碼,則可以在呼叫規格中內嵌提供 JavaScript 程式碼,如下所示
create or replace function date_to_epoch( "ts" timestamp ) return number as mle language javascript ; /
內嵌 JavaScript 程序有一些限制,例如它們無法匯入其他 MLE 模組(但它們可以要求隨附 MLE 的程式庫)。這就是為什麼您應該考慮使用 MLE 模組來撰寫更複雜的邏輯和/或使其在其他模組中可重複使用。現在,您可以直接從 SQL 呼叫
date_to_epoch()
函式,如此範例所示select date_to_epoch ( to_timestamp('2000-01-01 12:00:00', 'yyyy-mm-dd hh24:mi:ss') ) seconds_since_epoch /
-
您可以從其他模組呼叫 MLE 模組。通常在建立更複雜的邏輯或第三方模組時會發生這種情況。如在 Oracle 資料庫 23c 免費開發人員版本中使用 JavaScript 社群模組中所述,將常用的
validator.js
模組載入到資料庫中,並遵守授權和合規性等注意事項。現在,您可以如上文所述,從您最愛的內容傳遞網路 (CDN) 匯入模組的 ECMASCript (ESM) 版本。假設驗證器模組在資料庫中建立為validator_module
。您可以在自己的程式碼中使用該模組,如下所示create or replace mle module validator_usage_demo_module language javascript as import validator from 'validator'; export function emailCheck(str) { return validator.isEMail(str); }
將匯入名稱 (
validator
) 對應至模組的元素稱為MLE 環境。由於 MLE 無法使用檔案系統來解析匯入名稱,因此您必須明確宣告對應,如下所示create or replace mle env validator_env imports ( 'validator' module validator_module );
MLE 環境有助於將呼叫規格中的匯入名稱對應至模組。如果您想公開 SQL 函式
emailCheck()
,請建立下列呼叫規格create or replace function email_check(p_str varchar2) return boolean as mle module validator_usage_demo_module env validator_env signature 'emailCheck'; /
現在您幾乎可以在任何地方呼叫
email_check
。
類型轉換
了解類型轉換的工作方式非常重要 — 只要您的程式碼「離開」SQL 或 PL/SQL,類型就必須轉換為它們對應的 JavaScript 類型。(如需更多關於類型轉換的資訊,請參閱MLE 類型轉換。)
您可能知道 JavaScript Number
是以雙精確度 64 位元二進位格式 IEEE 754 值實作。它可能在儲存最大數字時發生問題,並導致精確度損失。以下範例示範了精確度可能損失的情況。
create table demo_table (
id number constraint pk_demo_table primary key,
value number not null
);
insert into demo_table(
id,
value
) values
(1, 0.1),
(2, 0.2);
create or replace procedure precision_loss(
"id1" demo_table.id%type,
"id2" demo_table.id%type)
as mle language javascript
;
/
begin precision_loss(1, 2); end;
/
precision_loss()
程序會在主控台中列印值 0.30000000000000004
(而不是 0.3
)。為了避免精確度損失,您可以遵循從 MLE JavaScript SQL 驅動程式呼叫 PL/SQL 和 SQL中列出的許多策略。其中一種方法是指示 SQL 驅動程式將數字傳回為類型 OracleNumber
:JavaScript 中 Oracle 的 Number 資料類型的包裝類型
create or replace procedure precision_preserved(
"id1" demo_table.id%type,
"id2" demo_table.id%type)
as mle language javascript
;
/
begin precision_preserved(1, 2); end;
/
precision_preserved()
程序現在會將正確的結果 (0.3
) 列印到主控台。包裝資料類型記錄在 mle-js-plsqltypes
模組中。每當您需要數字精確度時,請考慮使用其中一種包裝資料類型。
動態 JavaScript 執行
Oracle 資料庫 21c 以 DBMS_MLE
套件的形式引入了 MLE。它仍然可用,但其重點已轉移到框架開發人員和嵌入到 REPL (讀取-求值-列印迴圈) 系統中。APEX、資料庫動作和其他工具會在幕後使用該套件。
以下是如何使用 DBMS_MLE
進行編碼的基本範例
set serveroutput on;
declare
l_ctx dbms_mle.context_handle_t;
l_source_code clob;
begin
-- Create execution context for MLE execution
l_ctx := dbms_mle.create_context();
-- using q-quotes to avoid problems with unwanted string termination
l_source_code :=
q'~
const result = session.execute(
`select 'hello, world'`,
[],
{
outFormat: oracledb.OUT_FORMAT_ARRAY
}
);
const message = result.rows[0][0];
console.log(message);
~';
dbms_mle.eval(
context_handle => l_ctx,
language_id => 'JAVASCRIPT',
source => l_source_code,
source_name => 'example01'
);
dbms_mle.drop_context(l_ctx);
exception
when others then
dbms_mle.drop_context(l_ctx);
raise;
end;
/
匿名 PL/SQL 區塊會在主控台中列印 hello, world
。如果您想使用 MLE 內建模組以外的模組,則必須使用 MLE 環境。在先前建立的 validator_env
的基礎上,您可以使用 DBMS_MLE
來驗證電子郵件,如下所示
set serveroutput on;
declare
l_ctx dbms_mle.context_handle_t;
l_source_code clob;
begin
-- Create execution context for MLE execution and provide an environment
l_ctx := dbms_mle.create_context('VALIDATOR_ENV');
-- using q-quotes to avoid problems with unwanted string termination
l_source_code :=
q'~
(async() => {
const { default: validator } = await import ('validator');
const str = 'not an email address';
console.log(`Is ${str} a valid email address? ${validator.isEmail(str)}`);
})()
~';
dbms_mle.eval(
context_handle => l_ctx,
language_id => 'JAVASCRIPT',
source => l_source_code,
source_name => 'example02'
);
dbms_mle.drop_context(l_ctx);
exception
when others then
dbms_mle.drop_context(l_ctx);
raise;
end;
/
您無法使用靜態 import
陳述式,因為 DBMS_MLE 會以指令碼模式評估 JavaScript 程式碼。但是,您可以如上述程式碼片段所示使用動態匯入。
APEX 和資料庫動作是非常棒的工具,因為它們可讓您專注於撰寫 JavaScript (在上述範例中為
l_source_code
)。
相關文件
- Oracle® 資料庫 JavaScript 開發人員指南.
- Martin Bach 關於在 Oracle 資料庫中使用 JavaScript 的部落格文章。
- 使用 MLE 模組.