Using JavaScript Modules in MLE
JavaScript modules can be used in several different ways and can be managed using a set of Data Definition Language (DDL) commands.
JavaScript code provided in MLE modules can be used in the following ways:
-
JavaScript functions exported by an MLE modules can be published by creating a call specification known as an MLE module call. This allows the function to be called directly from SQL and PL/SQL.
-
Functionality exported by a JavaScript MLE module can be imported in other MLE JavaScript modules.
-
Code snippets in
DBMS_MLE
can import modules for dynamic invocation of JavaScript.
Before a user can create and execute MLE modules, several privileges must be granted.
See Also:
-
Overview of Importing MLE JavaScript Modules for more information about module calls
-
Overview of Dynamic MLE Execution for more information about
DBMS_MLE
and dynamic invocation of JavaScript code in the database -
System and Object Privileges Required for Working with JavaScript in MLE for more information about MLE-specific privileges
Topics
- Managing JavaScript Modules in the Database
SQL allows the creation of MLE modules as schema objects, assuming the necessary privileges are in place. - Preparing JavaScript code for MLE Module Calls
JavaScript modules in MLE follow the ECMAScript 6 standard for modules. Functions and variables expected to be consumed by users of the MLE module must be exported. - Additional Options for Providing JavaScript Code to MLE
The JavaScript source code of an MLE module can be specified inline with PL/SQL but can also be provided using a BFILE, BLOB, or CLOB, in which case the source file must be UTF8 encoded. - Specifying Module Version Information and Providing JSON Metadata
MLE modules may carry optional metadata in the form of a version string and free-form JSON-valued metadata. - Drop JavaScript Modules
TheDROP MLE MODULE
DDL statement is used for dropping an MLE module. - Alter JavaScript Modules
Attributes of an MLE module can be assigned or altered using theALTER MLE MODULE
statement. - Overview of Built-in JavaScript Modules
MLE provides a set of built-in JavaScript modules that are available for import in any execution context. - Dictionary Views Related to MLE JavaScript Modules
The Data Dictionary includes details about JavaScript modules.
Parent topic: MLE JavaScript Modules and Environments
Managing JavaScript Modules in the Database
SQL allows the creation of MLE modules as schema objects, assuming the necessary privileges are in place.
At a minimum, you need the CREATE MLE MODULE
privilege
to create or replace an MLE module in your
own schema. Additionally, you must have the execute privilege on the target
JavaScript language object.
See Also:
-
System and Object Privileges Required for Working with JavaScript in MLE for more information about MLE-specific privileges
-
Oracle Database Security Guide for more details about privileges and roles in Oracle Database
Topics
- Naming JavaScript Modules
Each JavaScript module name must be unique in the schema that it is created in. Unless a fully qualified name is used, the current user's schema is used. - Creating JavaScript Modules in the Database
JavaScript modules are created in the database using theCREATE MLE MODULE
DDL statement, specifying name and source code of the MLE module. - Storing JavaScript Code in Databases Using Single-Byte Character Sets
Character set standards and things to remember when using a single-byte character set with MLE. - Code Analysis
JavaScript syntax errors are flagged when an MLE module is created but a linting tool of your choice should still be used to perform analysis before executing theCREATE MLE MODULE
command.
Parent topic: Using JavaScript Modules in MLE
Naming JavaScript Modules
Each JavaScript module name must be unique in the schema that it is created in. Unless a fully qualified name is used, the current user's schema is used.
As with other schema object identifiers, the module name is case-sensitive if enclosed in double quotation marks. If the enclosing quotation marks are omitted, the name is implicitly converted to uppercase.
When choosing a unique name, note that MLE objects share the namespace with tables, views, materialized views, sequences, private synonyms, PL/SQL packages, functions, procedures, and cache groups.
Parent topic: Managing JavaScript Modules in the Database
Creating JavaScript Modules in the Database
JavaScript modules are created in the database using the CREATE MLE
MODULE
DDL statement, specifying name and source code of the MLE module.
As soon as an MLE module has been
created, it is persisted in the database dictionary. This is one of the differences
when compared with dynamic execution of JavaScript code using
DBMS_MLE
.
CREATE MLE MODULE
(without the OR REPLACE
clause)
throws an error if an MLE module with the
given name already exists. With CREATE OR REPLACE MLE MODULE
, the
existing module is replaced if it exists, otherwise a new one is created. When an
MLE module is replaced, privileges to it
do not need to be re-granted.
For those who are familiar with PL/SQL, note that this is exactly the same behavior experienced with PL/SQL program units.
If you do not wish to replace an existing module in the event the module
name is already in use, you can use the IF NOT EXISTS
clause rather
than CREATE OR REPLACE
. The syntax for this variation is shown in
Example 3-1. The IF NOT EXISTS
and OR REPLACE
clauses are
mutually exclusive.
See Also:
-
Oracle Database SQL Language Reference for the complete
CREATE MLE MODULE
syntax -
Oracle Database Development Guide for more information about using the
IF
[NOT
]EXISTS
syntax
Example 3-1 Creating a JavaScript Module in the Database
This example demonstrates the creation of an MLE module and the export of a simple JavaScript function.
CREATE MLE MODULE IF NOT EXISTS po_module LANGUAGE JAVASCRIPT AS
/**
* get the value of all line items in an order
* @param {array} lineItems - all the line items in a purchase order
* @returns {number} the total value of all line items in a purchase order
*/
export function orderValue(lineItems) {
return lineItems
.map( x => x.Part.UnitPrice * x.Quantity )
.reduce(
(accumulator, currentValue) => accumulator + currentValue, 0
);
}
/
The first line of this code block specifies the JavaScript module name as
po_module
. The remaining lines define the actual JavaScript
code. Note that in line with the ECMAScript standard, the export keyword indicates
the function to be exported to potential callers of the module. MLE accepts code adhering to the ECMAScript
2023 standard.
Parent topic: Managing JavaScript Modules in the Database
Storing JavaScript Code in Databases Using Single-Byte Character Sets
Character set standards and things to remember when using a single-byte character set with MLE.
JavaScript is encoded in Unicode. The Unicode Standard is a character encoding system that defines every character in most of the spoken languages in the world. It was developed to overcome limitations of other character-set encodings.
Oracle recommends creating databases using the AL32UTF8 character set. Using the AL32UTF8 character set in the database ensures the use of the latest version of the Unicode Standards and minimizes the potential for character-set conversion errors.
In case your database still uses a single-byte character set such as US7ASCII, WE8ISO8859-n, or WE8MSWIN1252, you must be careful not to use Unicode features in MLE JavaScript code. This is no different than handling other types of input data with such a database.
See Also:
Oracle Database Globalization Support Guide for more details about the Unicode Standard
Parent topic: Managing JavaScript Modules in the Database
Code Analysis
JavaScript syntax errors are flagged when an MLE module is created but a linting tool of your
choice should still be used to perform analysis before executing the CREATE MLE
MODULE
command.
When creating MLE modules in the database, you
should use a well-established toolchain in the same way other JavaScript projects are
governed. In this sense, the call to CREATE MLE MODULE
can be
considered a deployment step, similar to deploying a server application. Code checking
should be performed during a build step, for example by a continuous integration and
continuous deployment (CI/CD) pipeline, prior to deployment.
If a module is created using CREATE MLE MODULE
that includes syntax
errors in the JavaScript code, the module will be created but it will exist in an
invalid state. This check does not apply to any SQL statements called within the module,
so separate testing should still be performed to ensure that the code works as
expected.
It is considered an industry best practice to process code with a tool called a linter before checking it into a source-code repository. As with any other development project, you are free to choose the best option for yourself and your team. Some potential options include ESLint, JSHint, JSLint, and others that perform static code analysis to flag syntax errors, bugs, or otherwise problematic code. They can also be used to enforce a certain coding style. Many integrated development environments (IDEs) provide linting as a built-in feature, invoking the tool as soon as a file is saved to disk and flagging any issues.
In addition to executing linting dynamically, it is possible to automate the code analysis using highly automated DevOps environments to invoke linting as part of a build pipeline. This step usually occurs prior to submitting the JavaScript module to the database.
The aim is to trap as many potential issues as possible before they can produce problems at runtime. Unit tests can help further mitigate these risks and their inclusion into the development process have become an industry best practice. Regardless of the method you choose, the code analysis step occurs prior to submitting the JavaScript module to the database.
Parent topic: Managing JavaScript Modules in the Database
Preparing JavaScript code for MLE Module Calls
JavaScript modules in MLE follow the ECMAScript 6 standard for modules. Functions and variables expected to be consumed by users of the MLE module must be exported.
Those variables and functions not exported are considered private in the module. Example 3-3 demonstrates the use of both public and private functions in an MLE JavaScript module.
An ECMAScript module can import other ECMAScript modules using import statements or dynamic import calls. This functionality is present in MLE as well. Complementary metadata to MLE modules is provided in MLE environments.
Note that console output in MLE is facilitated
using the console object. By default, anything written to
console.log()
is routed to DBMS_OUTPUT
and
will end up on the screen.
JavaScript code like that in Example 3-1 cannot be accessed from SQL or PL/SQL without the help of call specifications. For now, you can think of a call specification as a PL/SQL program unit (function, procedure, or package) where its PL/SQL body is replaced with a reference to the JavaScript module and function, as shown in Example 3-2. For more information about call specifications, see MLE JavaScript Functions.
Example 3-2 Create a Call Specification for a Public Function
This example uses the module po_module
created in Example 3-1. A call specification for orderValue()
, the only function
exported in po_module
, can be written as follows:
CREATE OR REPLACE FUNCTION order_value(
p_line_items JSON
) RETURN NUMBER AS
MLE MODULE po_module
SIGNATURE 'orderValue';
/
Once the function is created, it is possible to calculate the value of a given purchase order:
SELECT
po.po_document.PONumber,
order_value(po.po_document.LineItems[*]) order_value
FROM
j_purchaseorder po;
Result:
PONUMBER ORDER_VALUE
---------- -----------
1600 279.3
672 359.5
Example 3-3 Public and Private Functions in a JavaScript Module
In addition to public (exported) functions, it is possible to add functions private
to the module. In this example, the calculation of the value is taken out of the
map()
function and moved to a separate function
(refactoring).
The first function in the following code,
lineItemValue()
, is considered private, whereas the second
function, orderValue()
, is public. The export keyword is provided
at the end of this code listing but can also appear as a prefix for variables and
functions, as seen in Example 3-1. Both variations are valid JavaScript syntax.
CREATE OR REPLACE MLE MODULE po_module LANGUAGE JAVASCRIPT AS
/**
* calculate the value of a given line item. Factored out of the public
* function to allow for currency conversions in a later step
* @param {number} unitPrice - the price of a single article
* @param {number} quantity - the quantity of articles ordered
* @returns {number} the monetary value of the line item
*/
function lineItemValue(unitPrice, quantity) {
return unitPrice * quantity;
}
/**
* get the value of all line items in an order
* @param {array} lineItems - all the line items in a purchase order
* @returns {number} the total value of all line items in a purchase order
*/
function orderValue(lineItems) {
return lineItems
.map( x => lineItemValue(x.Part.UnitPrice, x.Quantity) )
.reduce(
(accumulator, currentValue) => accumulator + currentValue, 0
);
}
export { orderValue }
/
Parent topic: Using JavaScript Modules in MLE
Additional Options for Providing JavaScript Code to MLE
The JavaScript source code of an MLE module can be specified inline with PL/SQL but can also be provided using a BFILE, BLOB, or CLOB, in which case the source file must be UTF8 encoded.
Creating MLE modules using the BFILE clause can cause problems with logical replication such as GoldenGate. In order for the DDL command to succeed on the target database, the same directory must exist on the target database. Furthermore, the same JavaScript file must be present in this directory. Failure to adhere to these conditions will cause the call to create the MLE module on the target database to fail.
A BLOB or a CLOB can also be used to create an MLE module as an alternative to using a BFILE. Example 3-5 shows how to create a JavaScript module using a CLOB. If you prefer to use a BLOB, the syntax is the same but the value of the BLOB will differ from that of a CLOB.
Example 3-4 Providing JavaScript Source Code Using a BFILE
In this example, JS_SRC_DIR
is a database directory object mapping
to a location on the local file system containing the module's source code in a file
called myJavaScriptModule.js
. When loading the file from the
directory location, MLE stores the source
code in the dictionary. Subsequent calls to the MLE module will not cause the source code to be refreshed from the disk.
If there is a new version of the module stored in
myJavaScriptModule.js
, it must be deployed using another call
to CREATE OR REPLACE MLE MODULE
.
CREATE MLE MODULE mod_from_bfile
LANGUAGE JAVASCRIPT
USING BFILE(JS_SRC_DIR,'myJavaScriptModule.js');
/
Example 3-5 Providing JavaScript Source Code Using a CLOB
CREATE OR REPLACE MLE MODULE mod_from_clob_inline
LANGUAGE JAVASCRIPT USING CLOB (
SELECT q'~
export function clob_hello(who){
return `hello, ${who}`;
}
~')
/
As an alternative, you also have the option of using JavaScript source code that is
stored in a table. This example variation assumes your schema features a table named
javascript_src
containing the JavaScript source code in column
src
along with some additional metadata. The following
statement fetches the CLOB and creates the module.
CREATE OR REPLACE MLE MODULE mod_from_clob_table
LANGUAGE JAVASCRIPT USING CLOB (
SELECT src
FROM javascript_src
WHERE
id = 1 AND
commit_hash = 'ac1fd40'
)
/
Staging tables like this can be found in environments where Continuous Integration (CI) pipelines are used to deploy JavaScript code to the database.
Parent topic: Using JavaScript Modules in MLE
Specifying Module Version Information and Providing JSON Metadata
MLE modules may carry optional metadata in the form of a version string and free-form JSON-valued metadata.
Both kinds of metadata are purely informational and do not influence the behavior of MLE. They are stored alongside the module in the data dictionary.
The VERSION
flag can be used as an internal reminder about what
version of the code is deployed. The information stored in the
VERSION
field allows developers and administrators to identify
the code in the version control system.
The format for JSON metadata is not bound to a schema; anything useful or informative
can be added by the developer. In case the MLE module is an aggregation of sources created by a tool such as
rollup.js
or webpack, it can be useful to store the associated
package-lock.json
file alongside the module.
The metadata field can be used to create a software bill of material (SBOM), allowing security teams and administrators to track information about deployed packages, especially in the case where third-party modules are used.
Tracking dependencies and vulnerabilities in the upstream repository supports easier identification of components in need of update after security vulnerabilities have been reported.
See Also:
- Dictionary Views Related to MLE JavaScript Modules
- Software Bill of Material for more information about using the metadata field to store a SBOM
Example 3-6 Specification of a VERSION string in CREATE MLE MODULE
CREATE OR REPLACE MLE MODULE version_mod
LANGUAGE JAVASCRIPT
VERSION '1.0.0.1.0'
AS
export function sq(num) {
return num * num;
}
/
Example 3-7 Addition of JSON Metadata to the MLE Module
This example uses the module version_mod
, created in
Example 3-6.
ALTER MLE MODULE version_mod
SET METADATA USING CLOB
(SELECT
'{
"name": "devel",
"lockfileVersion": 2,
"requires": true,
"packages": {}
}'
)
/
Parent topic: Using JavaScript Modules in MLE
Drop JavaScript Modules
The DROP MLE MODULE
DDL statement is used for dropping an
MLE module.
The DROP
statement specifies the name, and optionally
the schema of the module to be dropped. If a schema is not specified, the schema of
the current user is assumed.
Attempting to drop an MLE module that does not
exist causes an error to be thrown. In cases where this is not desirable, the
IF EXISTS
clause can be used. The DROP MLE
MODULE
command is silently skipped if the indicated MLE module does not exist.
Example 3-8 Drop an MLE Module
DROP MLE MODULE unused_mod;
Example 3-9 Drop an MLE Module Using IF EXISTS
DROP MLE MODULE IF EXISTS unused_mod;
Parent topic: Using JavaScript Modules in MLE
Alter JavaScript Modules
Attributes of an MLE module can be
assigned or altered using the ALTER MLE MODULE
statement.
The ALTER MLE MODULE
statement specifies the name, and
optionally the schema of the module to be altered. If the module name is not
prefixed with a schema, the schema of the current user is assumed.
Example 3-10 Alter an MLE Module
ALTER MLE MODULE change_mod
SET METADATA USING CLOB(SELECT'{...}');
Parent topic: Using JavaScript Modules in MLE
Overview of Built-in JavaScript Modules
MLE provides a set of built-in JavaScript modules that are available for import in any execution context.
Built-in modules are not deployed to the database as user-defined MLE modules, but are included as part of the MLE runtime. In particular, MLE provides the following three built-in JavaScript modules:
-
mle-js-oracledb
is the JavaScript MLE SQL Driver. -
mle-js-bindings
provides functionality to import and export values from the PL/SQL engine. -
mle-js-plsqltypes
provides definitions for the PL/SQL wrapper types. For example, JavaScript types that wrap PL/SQL and SQL types likeOracleNumber
. -
mle-js-fetch
provides a partial Fetch API polyfill, allowing developers to invoke external resources. -
mle-encode-base64
contains code to work with base64-encoded data. mle-js-encodings
provides functionality to handle text in UTF-8 and UTF-16 encodings.-
mle-js-plsql-ffi
provides functionality to handle PL/SQL packages, functions, and procedures as JavaScript objects.
These modules can be used to interact with the database and provide type conversions between the JavaScript engine and database engine.
See Also:
Server-Side JavaScript API Documentation for more information about the built-in JavaScript modules
Parent topic: Using JavaScript Modules in MLE
Dictionary Views Related to MLE JavaScript Modules
The Data Dictionary includes details about JavaScript modules.
Topics
- USER_SOURCE
Each JavaScript module's source code is externalized using the[USER | ALL | DBA | CDB]_SOURCE
dictionary views. - USER_MLE_MODULES
Metadata pertaining to JavaScript MLE modules are found in[USER | ALL | DBA | CDB]_MLE_MODULES
.
Parent topic: Using JavaScript Modules in MLE
USER_SOURCE
Each JavaScript module's source code is externalized using the [USER
| ALL | DBA | CDB]_SOURCE
dictionary views.
Modules created with references to the file system using the BFILE operator show the code at the time of the module's creation.
For more information about *_SOURCE
, see Oracle Database
Reference.
Example 3-11 Externalize JavaScript Module Source Code
SELECT
line,
text
FROM
USER_SOURCE
WHERE
name = 'PO_MODULE';
Example output:
LINE TEXT
----- -------------------------------------------------------------
1 /**
2 * calculate the value of a given line item. Factored out of the public
3 * function to allow for currency conversions in a later step
4 * @param {number} unitPrice - the price of a single article
5 * @param {number} quantity - the quantity of articles ordered
6 * @returns {number} the monetary value of the line item
7 */
8 function lineItemValue(unitPrice, quantity) {
9 return unitPrice * quantity;
10 }
11
12
13 /**
14 * get the value of all line items in an order
15 * @param {array} lineItems - all the line items in a purchase order
16 * @returns {number} the total value of all line items in a purchase order
17 */
18 export function orderValue(lineItems) {
19
20 return lineItems
21 .map( x => lineItemValue(x.Part.UnitPrice, x.Quantity) )
22 .reduce(
23 (accumulator, currentValue) => accumulator + currentValue, 0
24 );
25 }
Parent topic: Dictionary Views Related to MLE JavaScript Modules
USER_MLE_MODULES
Metadata pertaining to JavaScript MLE
modules are found in [USER | ALL | DBA | CDB]_MLE_MODULES
.
Any JSON metadata specified, version information, as well as language, name, and owner can be found in this view.
For more information about *_MLE_MODULES
, see Oracle Database
Reference.
Example 3-12 Find MLE Modules Defined in a Schema
SELECT MODULE_NAME, VERSION, METADATA
FROM USER_MLE_MODULES
WHERE LANGUAGE_NAME='JAVASCRIPT'
/
Example output:
MODULE_NAME VERSION METADATA
------------------------------ ---------- -----------
MY_MOD01 1.0.0.1
MY_MOD02 1.0.1.1
MY_MOD03
Parent topic: Dictionary Views Related to MLE JavaScript Modules