Introduction to the MLE JavaScript SQL Driver

The MLE JavaScript driver is closely modeled after the client-side Oracle SQL driver for Node.js, node-oracledb.

This close relationship between the server-side and client-side drivers reduces the effort required to port client-side JavaScript code from Node.js or Deno to the database. Functionality that cannot be reasonably mapped to the server-side environment is omitted from MLE and the MLE JavaScript driver and will throw errors.

This helps you identify those parts of the code requiring changes. Furthermore, the MLE JavaScript implementation is a pure JavaScript implementation. Certain features not part of the ECMAScript standard are unavailable in MLE, such as the window object as well as direct file and network I/O.

The mle-js-oracledb SQL driver defaults to a synchronous operating model and partially supports asynchronous execution via async/await.

Note:

Production code should adhere to industry best practices for error handling and logging, which have been omitted from this chapter's examples for the sake of clarity. Additionally, most examples feature the synchronous execution model due to its greater readability.

Note:

If you are running your JavaScript code in a restricted execution context, you cannot use the MLE JavaScript SQL driver. For more information about restricted execution contexts, see About Restricted Execution Contexts.

See Also:

Topics

Working with the MLE JavaScript Driver

Generic workflow for working with the MLE JavaScript driver.

At a high level, working with the MLE JavaScript driver is very similar to using the client-side node-oracledb driver, namely:

  1. Get a connection handle to the existing database session.
  2. Use the connection to execute a SQL statement.
  3. Check the result object returned by the statement executed, as well as any database errors that may have occurred.
  4. In the case of select statements, iterate over the resulting cursor.
  5. For statements manipulating data, decide whether to commit or roll the transaction back.

Applications that aren't ported from client-side Node.js or Deno can benefit from coding aids available in the MLE JavaScript SQL driver, such as many frequently used variables available in the global scope. These variables include the following:

  • oracledb for the OracleDb driver object
  • session for the default connection object
  • soda for the SodaDatabase object
  • plsffi for the foreign function interface (FFI) object

Additionally, the following types are available:

  • OracleNumber
  • OracleClob
  • OracleBlob
  • OracleTimestamp
  • OracleTimestampTZ
  • OracleDate
  • OracleIntervalDayToSecond
  • OracleIntervalYearToMonth

The availability of these objects in the global scope reduces the need to write boilerplate code. For details about global symbols available with the MLE JavaScript SQL driver, see Server-Side JavaScript API Documentation.

Connection Management in the MLE JavaScript Driver

Considerations when dealing with connection management in the MLE JavaScript driver.

Connection management in the MLE JavaScript driver is greatly simplified compared to the client driver. Because a database session will already exist when a JavaScript stored procedure is invoked, you don't need to worry about establishing and tearing down connections, connection pools, and secure credential management, to name just a few.

You need only be concerned with the getDefaultConnection() method from the mle-js-oracledb module or use the global session object.

Introduction to Executing SQL Statements

A single SQL or PL/SQL statement can be executed by the Connection class's execute() method. Query results can either be returned in a single JavaScript array or fetched in batches using a ResultSet object.

Fetching as ResultSet offers more control over the fetch operation whereas using arrays requires fewer lines of code and provides performance benefits unless the amount of data returned is enormous.

Example 7-1 Getting Started with the MLE JavaScript SQL Driver

The following code demonstrates how to import the MLE JavaScript SQL driver into the current module's namespace. This example is based on one provided in the node-oracledb documentation, A SQL SELECT statement in Node.js.

CREATE OR REPLACE MLE MODULE js_sql_mod LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

/**
 * Perform a lookup operation on the HR.DEPARTMENTS table to find all
 * departments managed by a given manager ID and print the result on
 * the console
 * @param {number} managerID the manager ID
*/

function queryExample(managerID) {

  if (managerID === undefined) {
    throw new Error (
        "Parameter managerID has not been provided to queryExample()"
    );
  }
  let connection;

  try {
    connection = oracledb.defaultConnection();

    const result = connection.execute(`
        SELECT manager_id, department_id, department_name
        FROM hr.departments
        WHERE manager_id = :id`,
        [
            managerID
        ], 
        {
            outFormat: oracledb.OUT_FORMAT_OBJECT
        }
    );
    if (result.rows.length > 0) {
        for (let row of result.rows) {
            console.log(`The query found a row:
                manager_id:      ${row.MANAGER_ID}
                department_id:   ${row.DEPARTMENT_ID}
                department_name: ${row.DEPARTMENT_NAME}`);
        }
    } else {
        console.log(`no data found for manager ID ${managerID}`);
    }

  } catch (err) {
    console.error(`an error occurred while processing the query: ${err.message}`);
  } 
}

export { queryExample };
/

The only function present in the module, queryExample(), selects a single row from the HR departments table using a bind variable by calling connection.execute(). The value of the bind variable is passed as a parameter to the function. Another parameter passed to connection.execute() indicates that each row returned by the query should be provided as a JavaScript object.

If data has been found for a given managerID, it is printed on the screen. By default, the call to console.log() is redirected to DBMS_OUTPUT. Should there be no rows returned a message indicating this fact is printed on the console.

The call specification in the following snippet allows the code to be invoked in the database.

CREATE OR REPLACE PROCEDURE p_js_sql_query_ex(
    p_manager_id number)
AS MLE MODULE js_sql_mod
SIGNATURE 'queryExample(number)';
/

Provided the defaults are still in place, invoking p_js_sql_query_ex displays the following:

SQL> set serveroutput on
SQL> EXEC p_js_sql_query_ex(103)
The query found a row:
manager_id:      103
department_id:   60
department_name: IT

See Also:

Server-Side JavaScript API Documentation for more information about the built-in JavaScript modules, including mle-js-oracledb

Example 7-2 Use Global Variables to Simplify SQL Execution

Example 7-1 can be greatly simplified for use with MLE. Variables injected into the global scope can be referenced, eliminating the need to import the mle-js-oracledb module. Additionally, because only a single function is defined in the module, an inline call specification saves even more typing.

CREATE OR REPLACE PROCEDURE js_sql_mod_simplified(
    "managerID" number
) AS MLE LANGUAGE JAVASCRIPT
{{
if (managerID === undefined || managerID === null){
    throw new Error (
        "Parameter managerID has not been provided to js_sql_mod_simplified()"
    );
}

const result = session.execute(`
    SELECT
        manager_id,
        department_id,
        department_name
    FROM
        hr.departments
    WHERE
        manager_id = :id`,
    [ managerID ]
);

if(result.rows.length > 0){
    for(let row of result.rows){
        console.log(
            `The query found a row:
             manager_id: ${row.MANAGER_ID}
             department_id: ${row.DEPARTMENT_ID}
             department_name: ${row.DEPARTMENT_NAME}`
        );
    }
} else {
    console.log(`no data found for manager ID ${managerID}`);
}
}};
/
Invoking js_sql_mod_simplified results in the following output:
SQL> set serveroutput on
SQL> exec js_sql_mod_simplified(100);

The query found a row:
manager_id:      100
department_id:   90
department_name: Executive

Processing Comparison Between node-oracledb and mle-js-oracledb

The node-oracledb documentation recommends the use of the async/await interface. Due to the nature of client-server interactions, most of the processing involved between node and the database is executed asynchronously.

The MLE JavaScript driver does not require asynchronous processing. Like the PL/SQL driver, this is thanks to the driver's location within the database. The MLE JavaScript driver understands the async/await syntax, however, it processes requests synchronously under the hood.

Unlike the node-oracledb driver, the MLE JavaScript SQL driver returns rows as objects (oracledb.OUT_FORMAT_OBJECT) rather than arrays (oracledb.OUTFORMAT_ARRAY) when using the ECMAScript 2023 syntax. Code still relying on the deprecated require syntax remains backwards compatible by returning rows as an array.

Note:

A promise-based interface is not provided with the MLE JavaScript driver.