Node-oracledb 1.12, the Node.js add-on for Oracle Database, is now onNPM.
Top features: LOBs as JavaScript Strings and Buffers. Pool connection 'aliveness' checking.
The two main features of node-oracledb 1.12 add functionality that
aids usability. You may not even notice one working in the
background, silently re-establishing pooled connections after network
outages. The other is a 'no brainer' that makes working with large
objects (LOBs) easier. You'll probably use it and think nothing of
it.
This release also contains a number of bug fixes and small changes.
Check the CHANGELOG for all the details.
LOB Enhancements
Adding to the existing support for using CLOBs and BLOBs as Node.js
Streams, now Strings and Buffers can be bound to CLOBs and BLOBs for
IN, IN OUT and OUT binds. CLOBs in queries can also be returned
directly as Strings. And streaming to and from Oracle's 'temporary
LOBs' is supported. Also in this release, Writeable LOB Streams now
conclude with a 'close' event.
The node-oracledb LOB documentation is worth reading. There are runnable examples
in the examples
directory.
To insert large data, just bind a Node.js String or Buffer to a
CLOB or BLOB column, respectively. For example:
var fs = require('fs');
var str = fs.readFileSync('example.txt', 'utf 8');
conn.execute(
"INSERT INTO mylobs (id, myclobcol) VALUES (:idbv, :cbv)",
{ idbv: 1,
cbv: str }, // type and direction are optional for IN binds
function(err, result)
{
if (err)
console.error(err.message);
else
console.log('CLOB inserted from example.txt');
. . .
You can also bind Strings and Buffers to PL/SQL LOB parameters as
IN, OUT and IN OUT binds.
To fetch CLOBs as Strings from queries, usefetchAsString
or fetchInfo
:
// return all CLOBs as Strings
oracledb.fetchAsString = [ oracledb.CLOB ];
conn.execute(
"SELECT mycol FROM mylobs WHERE id = 1",
function(err, result) {
if (err) { console.error(err.message); return; }
if (result.rows.length === 0)
console.error("No results");
else {
var clob = result.rows[0][0];
console.log(clob);
}
});
Currently BLOBs in queries must be streamed; we didn't want to hold
up the 1.12 release.
Use the right tool for the job: use Strings and Buffer for 'small'
LOBs, and use Streams for everything else. There is a theoretical
limit just under 1 GB in node-oracledb for manipulating LOBs as
Strings or Buffers. Luckily you wouldn't want to be allocating that
much memory in Node.js applications - it's better to stream. And
Node.js itself doesn't cope well with large data. It can get unstable
with Strings past the several hundred megabyte mark. (Note that if
node-oracledb is linked with Oracle client 11g libraries, than the
upper size of Strings and Buffers is just under the 64 KB mark: the
moral is use Oracle client 12c libraries - you can still connect to
Oracle Database 10.2, 11g and 12c).
For larger data you have multiple options for streaming. To insert
into a table column, use the existing RETURNING INTO method where you get an Oracle LOB locator and useNode.js Streams
functionality to put data into it. For fetching, use the existing LOB Streaming method and stream data out of the Lob.
If data is too big to bind as a String or Buffer to a PL/SQL
parameter, you can create a 'temporary LOB' in node-oracledb, stream
into it, and then bind it. This is new in node-oracledb 1.12
First, use the new connection.createLob()
method to create a temporary
LOB, here of type CLOB:
conn.createLob(oracledb.CLOB, function(err, templob) {
if (err) { . . . }
// ... else use templob
});
This creates an instance of a node-oracledb Lob class.
Once created, data can be inserted into the Lob. For example to
read a text file:
templob.on('error', function(err) { somecallback(err); });
// The data was loaded into the temporary LOB, so use it
templob.on('finish', function() { somecallback(null, templob); });
// copies the text from 'example.txt' to the temporary LOB
var inStream = fs.createReadStream('example.txt');
inStream.on('error', function(err) { . . . });
inStream.pipe(templob);
Now the LOB has been populated, it can be bound in somecallback()
to a PL/SQL IN parameter:
// For PROCEDURE lobs_in(p_id IN NUMBER, c_in IN CLOB)
conn.execute("BEGIN lobs_in (:id, :c); END",
{ id: 3,
c: templob }, // type and direction are optional for IN binds
function(err)
{
if (err) { return cb(err); }
console.log("Call completed");
return cb(null, conn, templob);
});
When the LOB is no longer needed, it must be closed with lob.close():
templob.close(function (err) {
if (err)
. . .
else
// success
});
Connection Pool Enhancements
The other main feature in node-oracledb 1.12 is connection pool
session 'aliveness' checking. This is enabled by default. You may
never notice it in action but it will improve application behavior
where networks are unstable, or database services become temporarily
unavailable.
When pooled connections have been established to the database, but
are idle in the connection pool not currently being used, there is a
chance that a network outage will make those connections unusable. A
common scenario is when a developer's laptop is closed for a while.
Unless node-oracledb is linked with Oracle 12.2 client libraries, any
node-oracledb pooled getConnection()
call could return
one of the unusable connections. For users of Oracle 11.2 or 12.1
client libraries, a new poolPingInterval
setting will do a 'ping' to the
database to check the connection is OK before returning that
connection to the application. If it isn't OK, then another
connection is returned.
There is the overhead of a 'round trip' to the database in doing a
ping, so there could be some impact on scalability. But the ping
setting is time based so there is no overhead on active pools. The
ping will only happen when a connection has been idle in the pool forpoolPingInterval
seconds. If a connection has been
recently used, it will be returned to the application without being
checked. The default interval is 60 seconds. The feature can be
configured to always-force the ping, to totally disable it, or to be
whatever interval meets your performance and quality-of-service
requirements. The value can be set when a pool is created, or turned
on globally:
oracledb.poolPingInterval = 60;
Applications should continue to detect and handle execution-time
errors, since execution errors may occur, or a network outage may
occur between getConnection()
and execute()
calls, or connections may have been in use and not released back to
the pool when the network dropped, or the idle time may have been
shorter than the ping interval setting and no ping performed. The new
feature is great at improving the reliability of connections in
unstable environments but is just one of the options and tools that
can be used to provide a highly available application.
The ping feature is a no-op when node-oracledb is linked with
Oracle 12.2 client libraries (independent of the database version at
the other end of the connection), since those libraries have an
always-enabled, lightweight connection check that removes the need for
node-oracledb to do its own ping. This adds even more certainty that
a connection will be valid at time of first use after agetConnection()
call. Oracle 12.2 is currently available
in Oracle Cloud.
The pool pinging documentation has more information.
Documentation Updates
If you haven't scanned the documentation for a while, take a look. Each release more and
more tips and information gets added.
A couple of community contributions from Leigh Schrandt and Nick Heiner regarding the README were gratefully received. Thank you!
Testing
Testing of node-oracle is also something I wanted to mention. Thetest suite continues to grow, as you can see when you run it. We
also have additional stress and loads tests - overall these extra test
take days to complete. There are no guarantees, of course, but
node-oracledb is more than solid.
Resources
Issues and questions about node-oracledb can be posted on GitHub. We
value your input to help prioritize work on the add-on. Drop us a
line!
node-oracledb installation instructions are here.
Node-oracledb documentation is here.
Finally, contributions to node-oracledb are more than welcome, seeCONTRIBUTING.