API Differences Between node-oracledb and mle-js-oracledb

There are several differences between node-oracledb and mle-js-oracledb, including the methods for handling connection management and type mapping.

See Also:

Server-Side JavaScript API Documentation for more information about JavaScript built-in modules

Topics

Synchronous API and Error Handling

Compared to node-oracledb, the mle-js-oracledb driver operates in a synchronous mode, throwing exceptions as they happen. If an asynchronous behavior is desired, calls to mle-js-oracledb can be wrapped into async functions.

During synchronous operations, API calls block until either a result or an error are returned. Errors caused by SQL execution are reported as JavaScript exceptions, otherwise they return the same properties as the node-oracledb Error object.

The following methods neither return a promise nor do they take a callback parameter. They either return the result or throw an exception.
  • connection.execute
  • connection.executeMany
  • connection.getStatementInfo
  • connection.getSodaDatabase
  • connection.commit
  • connection.rollback
  • resultset.close
  • resultset.getRow
  • resultset.getRows
The following method cannot be implemented in a synchronous way and is omitted in the MLE JavaScript driver.
  • connection.break
node-oracledb provides a LOB (Large Object) class to provide streaming access to LOB types. The LOB class implements the asynchronous Node.js Stream API and cannot be supported in the synchronous MLE JavaScript environment. Large objects are supported using an alternative API in the MLE JavaScript driver. For these reasons, the following LOB-related functionality is not supported.
  • connection.createLob
  • property oracledb.lobPrefetchSize
  • constant oracledb.BLOB
  • constant oracledb.CLOB
node-oracledb also implements asynchronous streaming of query results, another feature that's based on the Node.js Stream API. A streaming API cannot be represented in a synchronous interface as used by the MLE JavaScript driver, therefore the following functionality is not available.
  • connection.queryStream()
  • resultSet.toQueryStream()

Connection Handling

The method of connection handling with the MLE JavaScript driver is described.

All SQL statements that are executed via the server-side MLE JavaScript driver are executed in the current session that is running the JavaScript program. SQL statements are executed with the privileges of the user on whose behalf JavaScript code is executed. As in the node-oracledb API, JavaScript code using the MLE JavaScript driver must acquire a Connection object to execute SQL statements. However, the only connection available is the implicit connection to the current database session.

JavaScript code must acquire a connection to the current session using the MLE-specific oracledb.defaultConnection() method. On each invocation, it returns a connection object that represents the session connection. Creation of connections with the oracledb.createConnection method of node-oracledb is not supported by the MLE JavaScript driver; neither is the creation of a connection pool supported. Connection objects are implicitly closed and so the call to connection.close() is not available with the MLE JavaScript driver.

There is also no statement cursor caching with the MLE JavaScript driver and therefore there is no stmtCacheSize property.

The Real Application Cluster (RAC) option offers additional features, designed to increase availability of applications. These include Fast Application Notification (FAN) and Runtime Load Balancing (RLB), neither of which are supported by the MLE JavaScript driver.

Transaction Management

With respect to transaction management, server-side MLE JavaScript code behaves exactly like PL/SQL procedures and functions.

A JavaScript program is executed in the current transaction context of the calling SQL or PL/SQL statement. An ongoing transaction can be controlled by executing COMMIT, SAVEPOINT, or ROLLBACK commands. Alternatively, the methods connection.commit() and connection.rollback() can be used.

MLE JavaScript SQL driver connections cannot be explicitly closed. Applications relying on node-oracledb behavior where closing a connection performs a rollback of the transaction will need adjusting. The MLE JavaScript SQL driver neither performs implicit commit nor rollback of transactions.

The node-oracledb driver features an auto-commit flag, defaulting to false. The MLE JavaScript SQL driver does not implement this feature. If specified, the connection.execute() function ignores the parameter.

Type Mapping

The MLE JavaScript driver adheres to the behavior of node-oracledb with respect to conversions between PL/SQL types and JavaScript types.

By default, PL/SQL types map to native JavaScript types (except for BLOBs and CLOBs). Values fetched from query results are implicitly converted. See MLE Type Conversions for more details about MLE type mappings.

As with node-oracledb, the conversion from non-character data types and vice versa is directly impacted by the NLS session parameters. The MLE runtime locale has no impact on these conversions.

To avoid loss of precision when converting between native JavaScript types and PL/SQL data types, the MLE JavaScript driver introduces new wrapper types.
  • oracledb.ORACLE_NUMBER
  • oracledb.ORACLE_CLOB
  • oracledb.ORACLE_BLOB
  • oracledb.ORACLE_TIMESTAMP
  • oracledb.ORACLE_TIMESTAMP_TZ
  • oracledb.ORACLE_DATE
  • oracledb.ORACLE_INTERVAL_YM
  • oracledb.ORACLE_INTERVAL_DS

As with node-oracledb, the default mapping to JavaScript types may be overridden on a case-by-case basis using the fetchInfo property on connection.execute(). Type constants like oracledb.ORACLE_NUMBER may be used to override the type mapping for a specific NUMBER column in order to avoid implicit conversion and loss of precision.

Additionally, the JavaScript MLE SQL driver provides a way to change the default mapping of PL/SQL types globally. If the oracledb.fetchAsPlsqlWrapper property contains the corresponding type constant, Oracle values are fetched as SQL wrapper types previously described. As with the existing property oracledb.fetchAsString, this behavior can be overridden using fetchInfo and oracledb.DEFAULT. Because MLE JavaScript does not support a Buffer class, and instead uses Uint8Array, property oracledb.fetchAsBuffer from node-oracledb does not exist in mle-js-oracledb, which instead uses oracledb.fetchAsUint8Array.

Changing the type mapping to fetch JavaScript SQL wrapper types by default accounts for the following scenarios:
  • Oracle values are mainly moved between queries and DML statements, so that the type conversions between PL/SQL and JavaScript types are an unnecessary overhead.
  • It is crucial to avoid data loss.

Example 7-19 Using JavaScript Native Data Types vs Using Wrapper Types

This example demonstrates the effect of using JavaScript native data types for calculations. It also compares the loss of precision using JavaScript native types versus using wrapper types.

CREATE OR REPLACE MLE MODULE js_v_wrapper_mod
LANGUAGE JAVASCRIPT AS

/**
 *There is a potential loss of precision when using native
 *JavaScript types to perform certain calculations. This
 *is caused by the underlying implementation as a floating
 *point number
*/

export function precisionLoss(){
    
    let summand1 = session
        .execute(`SELECT 0.1 summand1`)
        .rows[0].SUMMAND1;

    let summand2 = session
        .execute(`SELECT 0.2 summand2`)
        .rows[0].SUMMAND2;

    const result = summand1 + summand2;

    console.log(`precisionLoss() result: ${result}`);
}

/**
 *Use an Oracle data type to preserve precision. The above
 *example can be rewritten using the OracleNumber type as
 *follows
*/
export function preservePrecision(){
    
    //instruct the JavaScript SQL driver to return results as
    //Oracle Number. This could have been done for individual
    //statements using the fetchInfo property - the global
    //change applies to this and all future calls
    oracledb.fetchAsPlsqlWrapper = [oracledb.NUMBER];
    let summand1 = session
        .execute(`SELECT 0.1 S1`)
        .rows[0].S1;
    
    let summand2 = session
        .execute(`SELECT 0.2 S2`)
        .rows[0].S2;

    const result = summand1 + summand2;

    console.log(`preservePrecision() result: ${result}`);
}
/

When executing the above functions, the difference in precision becomes immediately obvious.

precisionLoss() result: 0.30000000000000004
preservePrecsion() result: .3

Rather than setting the global oracledb.fetchAsPlsqlWrapper property, it is possible to override the setting per invocation of connection.execute(). Example 7-20 shows how precisionPreservedGlobal() can be rewritten by setting precision inline.

For information about functions available for use with type OracleNumber, see Server-Side JavaScript API Documentation.

Example 7-20 Overriding the Global oracledb.fetchAsPlsqlWrapper Property

This example extends Example 7-19 by showing how precisionPreservedGlobal() can be rewritten by preserving precision inline. It demonstrates that rather than setting the global oracledb.fetchAsPlsqlWrapper property, it is possible to override the setting per invocation of connection.execute().

CREATE OR REPLACE PROCEDURE fetch_info_example
AS MLE LANGUAGE JAVASCRIPT
{{
    let summand1 = session
        .execute(
            `SELECT 0.1 S1`,
            [],
            {
                fetchInfo:{
                    S1:{type: oracledb.ORACLE_NUMBER}
                }
            }
        )
        .rows[0].S1;
    
    let summand2 = session
        .execute(
            `SELECT 0.2 S2`,
            [],
            {
                fetchInfo:{
                    S2:{type: oracledb.ORACLE_NUMBER}
                }
            }
        )
        .rows[0].S2;

    const result = summand1 + summand2;

    console.log(`
    preservePrecision():
    summand1: ${summand1}
    summand2: ${summand2}
    result: ${result}
    `);
}};
/

Result:

preservePrecision():
summand1: .1
summand2: .2
result: .3

Unsupported Data Types

The MLE JavaScript driver does not currently support these data types:
  • LONG
  • LONG RAW
  • XMLType
  • BFILE
  • REF CURSOR

Miscellaneous Features Not Available with the MLE JavaScript SQL Driver

Differences between what features are available with the MLE JavaScript driver and with node-oracledb are described.

Error handling in the MLE JavaScript driver relies on the JavaScript exception framework rather than using a callback/promise as node-oracledb does. The error thrown by the MLE JavaScript SQL driver is identical to the Error object available with node-oracledb.

Several additional client-side features available in node-oracledb are not supported by the server-side MLE environment. The MLE JavaScript driver omits the API for these features.

The following features are currently unavailable:
  • Continuous Query Notification (CQN)
  • Advanced Queuing is not supported natively, the PL/SQL API can be used as a workaround
  • Connection.subscribe()
  • Connection.unsubscribe()
  • All Continuous Query Notification constants in the oracledb class
  • All Subscription constants in the oracledb class