Documentation, Test and Example updates for 6.6 release

This commit is contained in:
Sharad Chandran R 2024-06-21 17:36:23 +05:30
parent be408fa475
commit 5848805638
12 changed files with 398 additions and 22 deletions

View File

@ -13,16 +13,16 @@ node-oracledb `v6.6.0 <https://github.com/oracle/node-oracledb/compare/v6.5.1...
Thin Mode Changes
+++++++++++++++++
#) Added support to use IFILE parameter to embed custom
network configuration files in the main tnsnames.ora file.
#) Added support to use ``IFILE`` parameter to embed custom
network configuration files in the :ref:`tnsnames.ora <tnsadmin>` file.
#) Fixed bug which throws a ``TypeError: objType.attributes is not iterable``
error when :ref:`DbObject Class <dbobjectclass>` instance contains an
attribute of type ``SYS.XMLTYPE``.
#) Fixed bug which throws a ``TypeError: objType.attributes is not iterable``
error when :ref:`DbObject Class <dbobjectclass>` instance contains an
attribute of type ``SYS.XMLTYPE``.
#) Fixed bug which throws an ``NJS-130`` error when calling
:meth:`connection.getDbObjectClass()` with an object type name containing
``%ROWTYPE``.
#) Fixed bug which throws an ``NJS-130`` error when calling
:meth:`connection.getDbObjectClass()` with an object type name containing
``%ROWTYPE``.
#) Fixed bug which throws an ``NJS-112`` error during fetching of JSON and
vector columns after table recreation. This fix is similar to the one

View File

@ -489,7 +489,6 @@ This PL/SQL procedure can be called in node-oracledb using:
cursor: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{
prefetchRows: 1000, // tune the internal getRow() data fetch performance
fetchArraySize: 1000
}
);
@ -515,7 +514,6 @@ number of rows:
cursor: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{
prefetchRows: 200, // tune the getRows() call
fetchArraySize: 200
}
);

View File

@ -344,6 +344,8 @@ files affect connections and applications. The common files are:
- Description
* - ``tnsnames.ora``
- Contains Oracle Net Service names and Oracle Net options for databases that can be connected to, see :ref:`Net Service Names for Connection Strings <tnsnames>`. This file is only needed for advanced configuration. Not needed if connection strings use the :ref:`Easy Connect syntax <easyconnect>`. The `Oracle Net documentation on tnsnames.ora <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-7F967CE5-5498-427C-9390-4A5C6767ADAA>`__ has more information.
From version 6.6 onwards, node-oracledb recognizes the `IFILE <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-F8AC6FC6-F456-481F-8997-3B0E906BB745>`__ parameter that is used in the ``tnsnames.ora`` file to embed custom network configuration files.
* - ``sqlnet.ora``
- A configuration file controlling the network transport behavior. For example it can set call timeouts for :ref:`high availability <connectionha>`, or be used to :ref:`encrypt network traffic <securenetwork>`, or be used to configure logging and tracing. The `Oracle Net documentation on sqlnet.ora <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-19423B71-3F6C-430F-84CC-18145CC2A818>`__ has more information. Many settings can alternatively be specified using :ref:`Easy Connect syntax <easyconnect>`
@ -502,6 +504,8 @@ explicitly specified or a default location will be used. Do one of:
installation, in ``$ORACLE_HOME/network/admin`` or
``$ORACLE_BASE_HOME/network/admin``.
.. _oratzfile:
Using the Optional Time Zone File
---------------------------------
@ -527,11 +531,13 @@ time zones. By default the larger ``timezlrg_n.dat`` file is used. If
you want to use the smaller ``timezone_n.dat`` file, then set the
``ORA_TZFILE`` environment variable to the name of the file without any
directory prefix, for example ``export ORA_TZFILE=timezone_32.dat``.
With Oracle Instant Client 12.2 or later, you can also use an external
time zone file. Create a subdirectory ``oracore/zoneinfo`` under the
Instant Client directory, and move the file into it. Then set
``ORA_TZFILE`` to the file name, without any directory prefix. The
``genezi -v`` utility will show the time zone file in use.
From Oracle Instant Client 12.2, you can also use an external time zone
file. Create a subdirectory ``oracore/zoneinfo`` under the Instant Client
directory, and move the file into it. Then set ``ORA_TZFILE`` to the file
name, without any directory prefix. The ``genezi -v`` utility will show
the time zone file in use. With Oracle Instant Client 19.18 (or later), you
can alternatively place the external time zone file in any directory and then
set the ``ORA_TZFILE`` environment variable to the absolute path of the file.
The Oracle Database documentation contains more information about time
zone files, see `Choosing a Time Zone
@ -575,7 +581,7 @@ like ``LD_LIBRARY_PATH`` must be set before Node.js starts.
* - ``ORA_SDTZ``
- The default session time zone, see :ref:`Fetching Dates and Timestamps <datehandling>`.
* - ``ORA_TZFILE``
- The name of the Oracle time zone file to use. See the notes below.
- The name of the Oracle time zone file to use. See :ref:`oratzfile`.
* - ``ORACLE_HOME``
- The directory containing the Oracle Database software. This directory must be accessible by the Node.js process. This variable should *not* be set if node-oracledb uses Oracle Instant Client.
* - ``NLS_LANG``

View File

@ -672,6 +672,71 @@ The output shows the increased ship size:
See `plsqlrecord.js <https://github.com/oracle/node-oracledb/tree/main/
examples/plsqlrecord.js>`__ for a runnable example.
.. _plsqlrecordrowtype:
PL/SQL Records with %ROWTYPE Attribute
--------------------------------------
PL/SQL RECORDS with `%ROWTYPE <https://www.oracle.com/pls/topic/lookup?ctx=
dblatest&id=GUID-4E0B9FE2-909D-444A-9B4A-E0243B7FCB99>`__ attribute can be
bound for insertion and retrieval. This was introduced in node-oracledb 6.6.
The following example uses the PL/SQL package ``MY_PKG`` and table ``STAFF``:
.. code-block:: javascript
const stmts = [
`CREATE TABLE STAFF (ID NUMBER, NAME VARCHAR2(25))`,
`INSERT INTO STAFF VALUES (1, 'ADSA')`,
`CREATE OR REPLACE PACKAGE MY_PKG AS
TYPE STAFF_ARRAY IS TABLE OF STAFF%ROWTYPE
INDEX BY BINARY_INTEGER;
PROCEDURE prGetRecords(out_rec OUT MY_PKG.STAFF_ARRAY);
END MY_PKG;`,
`CREATE OR REPLACE PACKAGE BODY MY_PKG IS
PROCEDURE prGetRecords(out_rec OUT MY_PKG.STAFF_ARRAY)
IS
CURSOR c_STAFF IS
SELECT *
FROM STAFF;
BEGIN
OPEN c_STAFF;
FETCH c_STAFF BULK COLLECT INTO out_rec;
CLOSE c_STAFF;
END prGetRecords;
END MY_PKG;`
];
for (const s of stmts) {
await conn.execute(s);
}
You can pass the Oracle type name ``STAFF_ARRAY`` in the
:meth:`connection.getDbObjectClass()` method. To retrieve a STAFF_ARRAY record
back from the PL/SQL, the prototype object class is passed for the output
``bind`` type:
.. code-block:: javascript
const objClass = await conn.getDbObjectClass("MY_PKG.STAFF_ARRAY");
const result = await conn.execute(`CALL MY_PKG.prGetRecords(:out_rec)`,
{out_rec: {type: objClass, dir: oracledb.BIND_OUT}});
for (const val of result.outBinds.out_rec) {
console.log("\nRow contents:");
console.log(val);
}
This prints the following output::
Row contents:
[HR.STAFF%ROWTYPE] { ID: 1, NAME: 'ADSA' }
See `plsqlrowtype.js <https://github.com/oracle/node-oracledb/tree/main/
examples/plsqlrowtype.js>`__ for a runnable example.
.. _objexecmany:
Inserting or Passing Multiple Objects of the Same Type
@ -680,6 +745,67 @@ Inserting or Passing Multiple Objects of the Same Type
You can use ``executeMany()`` with objects. See :ref:`Binding Objects with
executeMany() <executemanyobjects>`.
.. _objxmltype:
Using XMLType Data in DbObjects
===============================
From version 6.6 onwards, you can use a :ref:`DbObject Class <dbobjectclass>`
instance with an attribute of type ``SYS.XMLTYPE`` in node-oracledb Thin mode.
Consider the following object ``NODB_XMLTYPE``:
.. code-block:: sql
CREATE TYPE nodb_xmltype AS OBJECT (
XMLDATA sys.xmltype);
The example below defines the XML data and queries this data from the
``NODB_XMLTYPE`` object.
.. code-block:: javascript
const XMLData =
'<Warehouse>\n ' +
'<WarehouseId>1</WarehouseId>\n ' +
'<WarehouseName>Melbourne, Australia</WarehouseName>\n ' +
'<Building>Owned</Building>\n ' +
'<Area>2020</Area>\n ' +
'<Docks>1</Docks>\n ' +
'<DockType>Rear load</DockType>\n ' +
'</Warehouse>\n';
const sql = `SELECT nodb_xmltype(sys.xmltype('${XMLData}')) FROM dual`;
const result = await connection.execute(sql);
console.log('XML Data:\n', result.rows[0][0].XMLDATA);
This query prints the XMLType data in the object ``nodb_xmltype``::
XML Data:
<Warehouse>
<WarehouseId>1</WarehouseId>
<WarehouseName>Melbourne, Australia</WarehouseName>
<Building>Owned</Building>
<Area>2020</Area>
<Docks>1</Docks>
<DockType>Rear load</DockType>
</Warehouse>
To validate the metadata inside the object, you can use:
.. code-block:: javascript
const xmlObjClass = result.metaData[0];
const pInObj = new xmlObjClass.dbTypeClass();
console.log('Data Type:\n', pInObj.attributes.XMLDATA.type);
This prints an output such as::
Data Type:
[DbType DB_TYPE_XMLTYPE]
See `xmltypeInDbObject.js <https://github.com/oracle/node-oracledb/tree/main/
examples/xmltypeInDbObject.js>`__ for a runnable example.
.. _objectlimitations:
Oracle Database Object Type Limitations

View File

@ -106,6 +106,7 @@ File Name | Description
[`plsqlproc.js`](plsqlproc.js) | How to call a PL/SQL procedure
[`plsqlrecord.js`](plsqlrecord.js) | Shows binding of PL/SQL RECORDS
[`plsqlvarrayrecord.js`](plsqlvarrayrecord.js) | Shows binding a VARRAY of RECORD in PL/SQL
[`plsqlrowtype.js`](plsqlrowtype.js) | Shows binding of PL/SQL %ROWTYPE object
[`raw.js`](raw.js) | Shows using a Buffer to insert and select a RAW
[`refcursor.js`](refcursor.js) | Shows using a ResultSet to fetch rows from a REF CURSOR
[`refcursortoquerystream.js`](refcursortoquerystream.js) | Converts a REF CURSOR returned from `execute()` to a query stream.
@ -134,3 +135,4 @@ File Name | Description
[`vectortype2.js`](vectortype2.js) | Insert data into VECTOR columns and verify vector operations.
[`version.js`](version.js) | Shows the node-oracledb version attributes
[`webapp.js`](webapp.js) | A simple web application using a connection pool
[`xmltypeInDbObject.js`](xmltypeInDbObject.js) | Work with XMLType data in DbObject (Thin mode only)

115
examples/plsqlrowtype.js Normal file
View File

@ -0,0 +1,115 @@
/* Copyright (c) 2024, Oracle and/or its affiliates. */
/******************************************************************************
*
* This software is dual-licensed to you under the Universal Permissive License
* (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
* 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
* either license.
*
* If you elect to accept the software under the Apache License, Version 2.0,
* the following applies:
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* NAME
* plsqlrowtype.js
*
* DESCRIPTION
* Shows binding of PL/SQL %ROWTYPE
*
*****************************************************************************/
'use strict';
Error.stackTraceLimit = 50;
const oracledb = require('oracledb');
const dbConfig = require('./dbconfig.js');
// This example runs in both node-oracledb Thin and Thick modes.
//
// Optionally run in node-oracledb Thick mode
if (process.env.NODE_ORACLEDB_DRIVER_MODE === 'thick') {
// Thick mode requires Oracle Client or Oracle Instant Client libraries.
// On Windows and macOS Intel you can specify the directory containing the
// libraries at runtime or before Node.js starts. On other platforms (where
// Oracle libraries are available) the system library search path must always
// include the Oracle library path before Node.js starts. If the search path
// is not correct, you will get a DPI-1047 error. See the node-oracledb
// installation documentation.
let clientOpts = {};
// On Windows and macOS Intel platforms, set the environment
// variable NODE_ORACLEDB_CLIENT_LIB_DIR to the Oracle Client library path
if (process.platform === 'win32' || (process.platform === 'darwin' && process.arch === 'x64')) {
clientOpts = { libDir: process.env.NODE_ORACLEDB_CLIENT_LIB_DIR };
}
oracledb.initOracleClient(clientOpts); // enable node-oracledb Thick mode
}
console.log(oracledb.thin ? 'Running in thin mode' : 'Running in thick mode');
async function run() {
let conn;
const table = 'STAFF';
const stmts = [
`CREATE TABLE ${table} (ID NUMBER, NAME VARCHAR2(25), AGE NUMBER(3) INVISIBLE)`,
`INSERT INTO ${table} VALUES (1, 'ADSA')`,
`CREATE OR REPLACE PACKAGE FOO_TEST AS
TYPE FOO_TMP_ARRAY IS TABLE OF ${table}%ROWTYPE
INDEX BY BINARY_INTEGER;
PROCEDURE prGetRecords(out_rec OUT FOO_TEST.FOO_TMP_ARRAY);
END FOO_TEST;`,
`CREATE OR REPLACE PACKAGE BODY FOO_TEST IS
PROCEDURE prGetRecords(out_rec OUT FOO_TEST.FOO_TMP_ARRAY)
IS
CURSOR c_${table} IS
SELECT *
FROM ${table};
BEGIN
OPEN c_${table};
FETCH c_${table} BULK COLLECT INTO out_rec;
CLOSE c_${table};
END prGetRecords;
END FOO_TEST;`
];
try {
conn = await oracledb.getConnection(dbConfig);
for (const s of stmts) {
await conn.execute(s);
}
const objClass = await conn.getDbObjectClass("FOO_TEST.FOO_TMP_ARRAY");
const result = await conn.execute(`CALL FOO_TEST.prGetRecords(:out_rec)`, {out_rec: {type: objClass, dir: oracledb.BIND_OUT}});
for (const val of result.outBinds.out_rec) {
console.log("\nRow contents:");
console.log(val);
}
console.log("\nColumn Information:");
console.log(objClass.prototype.elementTypeClass.prototype.attributes);
} catch (err) {
if (err.errorNum != 942)
console.log(err);
} finally {
if (conn) {
await conn.execute(`DROP TABLE ${table} PURGE`);
await conn.close();
}
}
}
run();

View File

@ -0,0 +1,96 @@
/* Copyright (c) 2024, Oracle and/or its affiliates. */
/******************************************************************************
*
* This software is dual-licensed to you under the Universal Permissive License
* (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
* 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
* either license.
*
* If you elect to accept the software under the Apache License, Version 2.0,
* the following applies:
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* NAME
* xmltypeInDbObject.js
*
* DESCRIPTION
* Work with XMLType data in DbObject (Thin mode only)
*
*****************************************************************************/
'use strict';
Error.stackTraceLimit = 50;
const oracledb = require('oracledb');
const dbConfig = require('./dbconfig.js');
// This example runs in node-oracledb Thin mode only.
if (!oracledb.thin) {
console.log ("This example does not run in Thick mode");
process.exit(0);
}
async function run() {
let conn;
const TYPE1 = 'NODB_TEST_XMLTYPE';
const XMLData =
'<Warehouse>\n ' +
'<WarehouseId>1</WarehouseId>\n ' +
'<WarehouseName>Melbourne, Australia</WarehouseName>\n ' +
'<Building>Owned</Building>\n ' +
'<Area>2020</Area>\n ' +
'<Docks>1</Docks>\n ' +
'<DockType>Rear load</DockType>\n ' +
'<WaterAccess>false</WaterAccess>\n ' +
'<RailAccess>N</RailAccess>\n ' +
'<Parking>Garage</Parking>\n ' +
'<VClearance>20</VClearance>\n' +
'</Warehouse>\n';
try {
conn = await oracledb.getConnection(dbConfig);
const sqlTypeCreate = `CREATE TYPE ${TYPE1} FORCE AS OBJECT (
XMLDATA sys.xmltype)`;
await conn.execute(sqlTypeCreate);
const sql = `SELECT ${TYPE1}(sys.xmltype('${XMLData}')) FROM dual`;
const result = await conn.execute(sql);
console.log('XML Data:');
console.log('---------');
console.log(result.rows[0][0].XMLDATA);
// Validate metadata
const xmlObjClass = result.metaData[0];
const pInObj = new xmlObjClass.dbTypeClass();
console.log('Data Type:');
console.log('----------');
console.log(pInObj.attributes.XMLDATA.type);
} catch (e) {
if (e.errorNum != 942)
console.log(e);
} finally {
if (conn) {
await conn.execute(`DROP TYPE ${TYPE1}`);
await conn.close();
}
}
}
run();

View File

@ -82,13 +82,13 @@ describe('33. dataTypeTimestamp1.js', function() {
await assist.verifyRefCursorWithFetchAsString(connection, tableName, dates);
});
}); // end of 33.1 suite
}); // 33.1
describe('33.2 stores null value correctly', function() {
it('33.2.1 testing Null, Empty string and Undefined', async function() {
await assist.verifyNullValues(connection, tableName);
});
});
}); // 33.2
describe('33.3 testing TIMESTAMP without TIME ZONE', function() {
const timestamps = assist.TIMESTAMP_STRINGS;
@ -124,6 +124,32 @@ describe('33. dataTypeTimestamp1.js', function() {
assert(typeof result.outBinds.bv, "string");
});
}); // end of 33.3 suite
}); // 33.3
describe('33.4 timestamp with time zone', () => {
const timezones = ['UTC', 'PST', 'IST', 'EST', 'GMT', 'JST', 'AEDT'];
const defaultORA_SDTZ = process.env.ORA_SDTZ;
after(function() {
if (!defaultORA_SDTZ && process.env.ORA_SDTZ)
delete process.env.ORA_SDTZ;
else process.env.ORA_SDTZ = defaultORA_SDTZ;
});
// This test checks for a bug when newer TZ files
// are missing in the IC package used for thick mode.
it('33.4.1 test with different session time zone', async () => {
for (const timezone of timezones) {
process.env.ORA_SDTZ = timezone;
const sql = `SELECT CURRENT_TIMESTAMP AS CT FROM DUAL`;
const binds = [];
const options = { outFormat: oracledb.OUT_FORMAT_OBJECT };
const result = await connection.execute(sql, binds, options);
const timestamp = result.rows[0].CT;
assert(timestamp instanceof Date);
}
});
}); // 33.4
});

View File

@ -64,7 +64,8 @@ describe('160. editionTest.js', function() {
before(async function() {
let isRunnable = Boolean(!oracledb.thin && dbConfig.test.DBA_PRIVILEGE);
let isRunnable = Boolean(!oracledb.thin && dbConfig.test.DBA_PRIVILEGE
&& !dbConfig.test.drcp);
if (isRunnable) {
const connection = await oracledb.getConnection(dbConfig);
if (connection.oracleServerVersion < 1202000100) {

View File

@ -48,7 +48,11 @@ describe('272. jsonDualityView1.js', function() {
const pwd = testsUtil.generateRandomPassword();
before(async function() {
isRunnable = await testsUtil.checkPrerequisites(2100000000, 2300000000);
isRunnable = (!(dbConfig.test.drcp || dbConfig.test.isCmanTdm));
if (isRunnable) {
isRunnable = await testsUtil.checkPrerequisites(2100000000, 2300000000);
isRunnable = isRunnable && dbConfig.test.DBA_PRIVILEGE;
}
if (!isRunnable) {
this.skip();
}

View File

@ -48,7 +48,7 @@ describe('273. jsonDualityView2.js', function() {
`)`;
before(async function() {
isRunnable = (!(dbConfig.test.drcp && dbConfig.test.isCmanTdm));
isRunnable = (!(dbConfig.test.drcp || dbConfig.test.isCmanTdm));
if (isRunnable) {
isRunnable = await testsUtil.checkPrerequisites(2100000000, 2300000000);
isRunnable = isRunnable && dbConfig.test.DBA_PRIVILEGE;

View File

@ -573,6 +573,8 @@ Overview of node-oracledb functional tests
32.3.1 SELECT query - original data
33.3.2 SELECT query - formatted data for comparison
33.3.3 returns scalar types from PL/SQL block
33.4 timestamp with time zone
33.4.1 test with different session time zone
34. dataTypeTimestamp2.js
34.1 Testing JavaScript Date with database TIMESTAMP(p)