Updates for 1.9.0 Development branch

This commit is contained in:
Christopher Jones 2016-04-19 13:56:22 +10:00
parent db5950f1cf
commit 8c52101005
27 changed files with 2523 additions and 575 deletions

View File

@ -1,5 +1,35 @@
# Change Log
## node-oracledb v1.9.0 Development (19 Apr 2016)
** Note this is a development release: features are subject to change.**
- Added Promise support. All asynchronous functions can now return
promises. By default the standard Promise library is used for Node
0.12, 4 and 5. This can be overridden.
- Added a `toQueryStream()` method for ResultSets, letting REF CURSORS
be transformed into Readable Streams.
- Added an experimental query Stream `_close()` method. It allows query
streams to be closed without needing to fetch all the data. It is
not for production use.
- Added aliases `pool.close()` and `connection.close()` for
`pool.terminate()` and `connection.release()` respectively.
- Some method parameter validation checks, such as the number or types
of parameters, will now throw errors synchronously instead of
returning errors via the callback.
- Removed an extra call to `getRows()` made by `queryStream()` at
end-of-fetch.
- Some random crashes caused by connections being garbage collected
while still in use should no longer occur.
- Regularized NJS error message capitalization.
## node-oracledb v1.8.0 (24 Mar 2016)
- Added `connection.queryStream()` for returning query results using a

View File

@ -402,47 +402,13 @@ Download the [Node.js package](http://nodejs.org) for OS X 64-bit and install it
### 5.3 Install the free Oracle Instant Client 'Basic' and 'SDK' ZIPs
Do either of the options given in [5.3.1](#instosxICroot) or [5.3.2](#instosxICuser).
Follow the steps in either [5.3.1](#instosxICuser) or [5.3.2](#instosxICroot).
### <a name="instosxICroot"></a> 5.3.1 Install Instant Client in /opt
This first installation option puts Instant Client in the default
location used by the node-oracledb installer. It requires root
access. If you don't want to update system directories then follow
the alternative steps in [5.3.2](#instosxICuser).
Download the free **Basic** and **SDK** ZIPs from
[Oracle Technology Network](http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html)
and
[install them](http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html#ic_osx_inst)
into the same directory:
```
sudo su -
unzip instantclient-basic-macos.x64-11.2.0.4.0.zip
unzip instantclient-sdk-macos.x64-11.2.0.4.0.zip
mkdir /opt/oracle
mv instantclient_11_2 /opt/oracle/instantclient
ln -s /opt/oracle/instantclient/libclntsh.dylib.11.1 /opt/oracle/instantclient/libclntsh.dylib
```
Link the OCI libraries into the default library path:
```
ln -s /opt/oracle/instantclient/{libclntsh.dylib.11.1,libnnz11.dylib,libociei.dylib} /usr/local/lib/
```
Continue with [5.4](#instosxICaddon).
### <a name="instosxICuser"></a> 5.3.2 Install Instant Client in a user directory
This is an alternative to [5.3.1](#instosxICroot) that does not require root access.
### <a name="instosxICuser"></a> 5.3.1 Install Instant Client in a user directory
Download the free **Basic** and **SDK** 64-bit ZIPs from
[Oracle Technology Network](http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html)
and
[install them](http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html#ic_osx_inst)
into the same directory:
and unzip them somewhere under your home directory:
```
unzip instantclient-basic-macos.x64-11.2.0.4.0.zip
@ -469,6 +435,34 @@ export OCI_INC_DIR=~/instantclient_11_2/sdk/include
These variables are only needed during installation.
Continue with [5.4](#instosxICaddon).
### <a name="instosxICroot"></a> 5.3.2 Install Instant Client in /opt
This alternative to [5.3.1](#instosxICroot) requires root access. It
puts Instant Client in the default location used by the node-oracledb
installer. If you don't want to update system directories then follow
the steps in [5.3.1](#instosxICuser) instead.
Download the free **Basic** and **SDK** ZIPs from
[Oracle Technology Network](http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html)
and install them into the same directory:
```
sudo su -
unzip instantclient-basic-macos.x64-11.2.0.4.0.zip
unzip instantclient-sdk-macos.x64-11.2.0.4.0.zip
mkdir /opt/oracle
mv instantclient_11_2 /opt/oracle/instantclient
ln -s /opt/oracle/instantclient/libclntsh.dylib.11.1 /opt/oracle/instantclient/libclntsh.dylib
```
Link the OCI libraries into the default library path:
```
ln -s /opt/oracle/instantclient/{libclntsh.dylib.11.1,libnnz11.dylib,libociei.dylib} /usr/local/lib/
```
### <a name="instosxICaddon"></a> 5.4 Install the add-on
If you are behind a firewall you may need to set your proxy, for

View File

@ -1,11 +1,11 @@
# node-oracledb version 1.8
# node-oracledb version 1.9
## <a name="about"></a> About node-oracledb
The node-oracledb add-on for Node.js powers high performance Oracle
Database applications.
Use node-oracledb to connect Node.js 0.10, 0.12, 4 LTS and 5 to
Use node-oracledb to connect Node.js 0.10, 0.12, 4 and 5 to
Oracle Database.
The add-on is stable, well documented, and has a comprehensive test suite.
@ -15,11 +15,12 @@ The node-oracledb project is open source and maintained by Oracle Corp. The hom
### Node-oracledb supports:
- [Promises](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#promiseoverview), Callbacks and Streams
- [SQL and PL/SQL execution](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#sqlexecution)
- Fetching of query results by [callbacks](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#resultsethandling) or [streams](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#streamingresults)
- [REF CURSORs](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#refcursors)
- [Large Objects: CLOBs and BLOBs](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#lobhandling)
- [Query results as JavaScript objects or array ](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#queryoutputformats)
- Oracle Database 12.1 [JSON datatype](http://docs.oracle.com/database/121/ADXDB/json.htm#ADXDB6246)
- [Query results as JavaScript objects or arrays](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#queryoutputformats)
- [Smart mapping between JavaScript and Oracle types with manual override available](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#typemap)
- [Data binding using JavaScript objects or arrays](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#bind)
- [Transaction Management](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#transactionmgt)
@ -30,11 +31,15 @@ The node-oracledb project is open source and maintained by Oracle Corp. The hom
- [Statement Caching](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#stmtcache)
- [Client Result Caching](http://docs.oracle.com/database/121/ADFNS/adfns_perf_scale.htm#ADFNS464)
- [End-to-end Tracing, Mid-tier Authentication, and Auditing](https://github.com/oracle/node-oracledb/blob/master/doc/api.md#endtoend)
- High Availability Features
- Oracle High Availability Features
- [Fast Application Notification (FAN)](http://docs.oracle.com/database/121/ADFNS/adfns_avail.htm#ADFNS538)
- [Runtime Load Balancing (RLB)](http://docs.oracle.com/database/121/ADFNS/adfns_perf_scale.htm#ADFNS515)
- [Transparent Application Failover (TAF)](http://docs.oracle.com/database/121/ADFNS/adfns_avail.htm#ADFNS534)
Various Oracle Database and Oracle Client versions, can be used.
Oracle's cross-version compatibility allows one node-oracledb
installation to connect to different database versions.
We are actively working on supporting the best Oracle Database
features, and on functionality requests from
[users involved in the project](https://github.com/oracle/node-oracledb/issues).
@ -45,7 +50,7 @@ Prerequisites:
- [Python 2.7](https://www.python.org/downloads/)
- C Compiler with support for C++ 11 (Xcode, gcc, Visual Studio or similar)
- The small, free [Oracle Instant Client](http://www.oracle.com/technetwork/database/features/instant-client/index-100365.html) "basic" and "SDK" packages if your database is remote. Or use a locally installed database such as the free [Oracle XE](http://www.oracle.com/technetwork/database/database-technologies/express-edition/overview/index.html) release
- The small, free [Oracle Instant Client](http://www.oracle.com/technetwork/database/features/instant-client/index-100365.html) "basic" and "SDK" packages if your database is remote. Or use the libraries and headers from a locally installed database such as the free [Oracle XE](http://www.oracle.com/technetwork/database/database-technologies/express-edition/overview/index.html) release
- Set `OCI_LIB_DIR` and `OCI_INC_DIR` during installation if the Oracle libraries and headers are in a non-default location
Run `npm install oracledb` to install from the [NPM registry](https://www.npmjs.com/package/oracledb).
@ -56,7 +61,7 @@ See [INSTALL](https://github.com/oracle/node-oracledb/tree/master/INSTALL.md) fo
There are examples in the [examples](https://github.com/oracle/node-oracledb/tree/master/examples) directory.
### A simple query example:
### A simple query example with callbacks:
```javascript
var oracledb = require('oracledb');
@ -90,6 +95,8 @@ With Oracle's sample HR schema, the output is:
[ [ 60, 'IT' ], [ 90, 'Executive' ], [ 100, 'Finance' ] ]
```
Node Promises can also be used.
## <a name="doc"></a> Documentation
See [Documentation for the Oracle Database Node.js Add-on](https://github.com/oracle/node-oracledb/tree/master/doc/api.md).
@ -98,9 +105,9 @@ See [Documentation for the Oracle Database Node.js Add-on](https://github.com/or
See [CHANGELOG](https://github.com/oracle/node-oracledb/tree/master/CHANGELOG.md)
## <a name="testing"></a> Testsuite
## <a name="testing"></a> Test Suite
To run the included testsuite see [test/README](https://github.com/oracle/node-oracledb/tree/master/test/README.md).
To run the included test suite see [test/README](https://github.com/oracle/node-oracledb/tree/master/test/README.md).
## <a name="contrib"></a> Contributing

File diff suppressed because it is too large Load Diff

59
examples/promises.js Normal file
View File

@ -0,0 +1,59 @@
/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. */
/******************************************************************************
*
* You may not use the identified files except in compliance with the Apache
* License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at
* http://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
* promises.js
*
* DESCRIPTION
* Executes a basic query using promises instead of the callback pattern.
*
* Scripts to create the HR schema can be found at:
* https://github.com/oracle/db-sample-schemas
*
*****************************************************************************/
var oracledb = require('oracledb');
var dbConfig = require('./dbconfig.js');
oracledb.getConnection(
{
user : dbConfig.user,
password : dbConfig.password,
connectString : dbConfig.connectString
})
.then(function(connection) {
return connection.execute(
"SELECT department_id, department_name " +
"FROM departments " +
"WHERE department_id = :did",
[180]
)
.then(function(result) {
console.log(result.metaData);
console.log(result.rows);
return connection.release();
})
.catch(function(err) {
console.log(err.message);
return connection.release();
});
})
.catch(function(err) {
console.error(err.message);
});

View File

@ -19,7 +19,9 @@
* refcursor.js
*
* DESCRIPTION
* Shows using a Result Set to fetch rows from a REF CURSOR
* Shows using a ResultSet to fetch rows from a REF CURSOR using getRows().
* Streaming is also possible (this is not shown).
*
* Uses Oracle's sample HR schema.
* Use demo.sql to create the required procedure or do:
*

View File

@ -0,0 +1,90 @@
/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. */
/******************************************************************************
*
* You may not use the identified files except in compliance with the Apache
* License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at
* http://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
* refcursortoquerystream.js
*
* DESCRIPTION
* Converts a refcursor (returned from execute) to a query stream for an
* alternative means of processing instead of using resultSet.getRows().
*
* Scripts to create the HR schema can be found at:
* https://github.com/oracle/db-sample-schemas
*
*****************************************************************************/
var oracledb = require('oracledb');
var dbConfig = require('./dbconfig.js');
oracledb.getConnection(
{
user : dbConfig.user,
password : dbConfig.password,
connectString : dbConfig.connectString
},
function(err, connection) {
if (err) {
console.error(err.message);
return;
}
connection.execute(
"BEGIN"
+ " OPEN :cursor FOR SELECT department_id, department_name FROM departments;"
+ "END;",
{
cursor: { type: oracledb.CURSOR, dir : oracledb.BIND_OUT }
},
function(err, result) {
var cursor;
var queryStream;
if (err) {
console.error(err.message);
doRelease(connection);
return;
}
cursor = result.outBinds.cursor;
queryStream = cursor.toQueryStream();
queryStream.on('data', function (row) {
console.log(row);
});
queryStream.on('error', function (err) {
console.error(err.message);
doRelease(connection);
});
queryStream.on('end', function () {
doRelease(connection);
});
}
);
}
);
function doRelease(connection) {
connection.release(
function(err) {
if (err) {
console.error(err.message);
}
}
);
}

View File

@ -17,16 +17,44 @@
*
*****************************************************************************/
'use strict';
var resultset = require('./resultset.js');
var Stream = require('./resultset-read-stream');
var QueryStream = require('./querystream.js');
var nodbUtil = require('./util.js');
// The queryStream function is similar to execute except that it immediately
// returns a readable stream.
// returns a QueryStream.
function queryStream(sql, binding, options) {
var self = this;
var stream;
stream = new Stream(self, sql, binding, options);
nodbUtil.assert(arguments.length > 0 && arguments.length < 4, 'NJS-009');
nodbUtil.assert(typeof sql === 'string', 'NJS-006', 1);
if (binding) {
nodbUtil.assert(nodbUtil.isObjectOrArray(binding), 'NJS-006', 2);
}
if (options) {
nodbUtil.assert(nodbUtil.isObject(options), 'NJS-006', 3);
}
binding = binding || [];
options = options || {};
options.resultSet = true;
stream = new QueryStream(null, self._oracledb);
self._execute(sql, binding, options, function(err, result) {
if (err) {
stream._open(err, null);
} else {
resultset.extend(result.resultSet, self._oracledb);
stream._open(null, result.resultSet);
}
});
return stream;
}
@ -39,24 +67,45 @@ function execute(a1, a2, a3, a4) {
var executeCb;
var custExecuteCb;
// Added this check so that node doesn't hang if no arguments are passed.
if (arguments.length < 2 || arguments.length > 4) {
if (arguments.length && typeof arguments[arguments.length - 1] === 'function') {
arguments[arguments.length - 1](new Error('NJS-009: invalid number of parameters'));
return;
} else {
throw new Error('NJS-009: invalid number of parameters');
}
nodbUtil.assert(arguments.length > 1 && arguments.length < 5, 'NJS-009');
nodbUtil.assert(typeof a1 === 'string', 'NJS-006', 1);
switch (arguments.length) {
case 2:
nodbUtil.assert(typeof a2 === 'function', 'NJS-006', 2);
break;
case 3:
nodbUtil.assert(nodbUtil.isObjectOrArray(a2), 'NJS-006', 2);
nodbUtil.assert(typeof a3 === 'function', 'NJS-006', 3);
break;
case 4:
nodbUtil.assert(nodbUtil.isObjectOrArray(a2), 'NJS-006', 2);
nodbUtil.assert(nodbUtil.isObject(a3), 'NJS-006', 3);
nodbUtil.assert(typeof a4 === 'function', 'NJS-006', 4);
break;
}
custExecuteCb = function(err, result) {
var outBindsKeys;
var outBindsIdx;
if (err) {
executeCb(err);
return;
}
// Need to extend resultsets which may come from either the query results
// or outBinds.
if (result.resultSet) {
resultset.extend(result.resultSet);
resultset.extend(result.resultSet, self._oracledb);
} else if (result.outBinds) {
outBindsKeys = Object.keys(result.outBinds);
for (outBindsIdx = 0; outBindsIdx < outBindsKeys.length; outBindsIdx += 1) {
if (result.outBinds[outBindsKeys[outBindsIdx]] instanceof self._oracledb.ResultSet) {
resultset.extend(result.outBinds[outBindsKeys[outBindsIdx]], self._oracledb);
}
}
}
executeCb(null, result);
@ -79,16 +128,22 @@ function execute(a1, a2, a3, a4) {
}
// This commit function is just a place holder to allow for easier extension later.
function commit() {
function commit(commitCb) {
var self = this;
nodbUtil.assert(arguments.length === 1, 'NJS-009');
nodbUtil.assert(typeof commitCb === 'function', 'NJS-006', 1);
self._commit.apply(self, arguments);
}
// This rollback function is just a place holder to allow for easier extension later.
function rollback() {
function rollback(rollbackCb) {
var self = this;
nodbUtil.assert(arguments.length === 1, 'NJS-009');
nodbUtil.assert(typeof rollbackCb === 'function', 'NJS-006', 1);
self._rollback.apply(self, arguments);
}
@ -99,23 +154,31 @@ function rollback() {
function release(releaseCb) {
var self = this;
// _pool will only exist on connections obtained from a pool.
if (self._pool && self._pool.queueRequests !== false) {
self._release(function(err) {
releaseCb(err);
nodbUtil.assert(arguments.length === 1, 'NJS-009');
nodbUtil.assert(typeof releaseCb === 'function', 'NJS-006', 1);
self._release(function(err) {
releaseCb(err);
// pool will only exist for connections obtained from a pool.
if (self._pool && self._pool.queueRequests !== false) {
self._pool._onConnectionRelease();
});
} else {
self._release(releaseCb);
}
}
// Need to maintain a reference to the connection instance to ensure that the
// garbage collector doesn't destroy it too soon.
self = undefined;
});
}
// This release function is just a place holder to allow for easier extension later.
// It's attached to the module as break is a reserved word.
module.break = function() {
module.break = function(breakCb) {
var self = this;
nodbUtil.assert(arguments.length === 1, 'NJS-009');
nodbUtil.assert(typeof breakCb === 'function', 'NJS-006', 1);
self._break.apply(self, arguments);
};
@ -143,7 +206,7 @@ function extend(conn, oracledb, pool) {
writable: true
},
execute: {
value: execute,
value: nodbUtil.promisify(execute),
enumerable: true,
writable: true
},
@ -151,7 +214,7 @@ function extend(conn, oracledb, pool) {
value: conn.commit
},
commit: {
value: commit,
value: nodbUtil.promisify(commit),
enumerable: true,
writable: true
},
@ -159,7 +222,7 @@ function extend(conn, oracledb, pool) {
value: conn.rollback
},
rollback: {
value: rollback,
value: nodbUtil.promisify(rollback),
enumerable: true,
writable: true
},
@ -167,7 +230,12 @@ function extend(conn, oracledb, pool) {
value: conn.release
},
release: {
value: release,
value: nodbUtil.promisify(release),
enumerable: true,
writable: true
},
close: { // alias for release
value: nodbUtil.promisify(release),
enumerable: true,
writable: true
},
@ -175,7 +243,7 @@ function extend(conn, oracledb, pool) {
value: conn.break
},
break: {
value: module.break,
value: nodbUtil.promisify(module.break),
enumerable: true,
writable: true
}

View File

@ -17,6 +17,8 @@
*
*****************************************************************************/
'use strict';
var Duplex = require('stream').Duplex;
var util = require('util');

View File

@ -17,11 +17,14 @@
*
*****************************************************************************/
'use strict';
var oracledbCLib;
var oracledbInst;
var Lob = require('./lob.js').Lob;
var pool = require('./pool.js');
var connection = require('./connection.js');
var nodbUtil = require('./util.js');
try {
oracledbCLib = require('../build/Release/oracledb');
@ -43,6 +46,10 @@ oracledbCLib.Oracledb.prototype.newLob = function(iLob) {
function createPool(poolAttrs, createPoolCb) {
var self = this;
nodbUtil.assert(arguments.length === 2, 'NJS-009');
nodbUtil.assert(nodbUtil.isObject(poolAttrs), 'NJS-006', 1);
nodbUtil.assert(typeof createPoolCb === 'function', 'NJS-006', 2);
self._createPool(poolAttrs, function(err, poolInst) {
if (err) {
createPoolCb(err);
@ -61,6 +68,10 @@ function createPool(poolAttrs, createPoolCb) {
function getConnection(connAttrs, createConnectionCb) {
var self = this;
nodbUtil.assert(arguments.length === 2, 'NJS-009');
nodbUtil.assert(nodbUtil.isObject(connAttrs), 'NJS-006', 1);
nodbUtil.assert(typeof createConnectionCb === 'function', 'NJS-006', 2);
self._getConnection(connAttrs, function(err, connInst) {
if (err) {
createConnectionCb(err);
@ -83,6 +94,9 @@ function extend(oracledb) {
Object.defineProperties(
oracledb,
{
_oracledb: { // Known to be used in util.js' promisify function.
value: oracledb
},
DEFAULT: {
value: 0,
enumerable: true
@ -135,6 +149,11 @@ function extend(oracledb) {
value: 4002,
enumerable: true
},
Promise: {
value: global.Promise,
enumerable: true,
writable: true
},
Oracledb: {
value: oracledbCLib.Oracledb,
enumerable: true
@ -169,7 +188,7 @@ function extend(oracledb) {
value: oracledb.createPool
},
createPool: {
value: createPool,
value: nodbUtil.promisify(createPool),
enumerable: true,
writable: true
},
@ -177,7 +196,7 @@ function extend(oracledb) {
value: oracledb.getConnection
},
getConnection: {
value: getConnection,
value: nodbUtil.promisify(getConnection),
enumerable: true,
writable: true
}
@ -185,7 +204,7 @@ function extend(oracledb) {
);
}
oracledbInst = new oracledbCLib.Oracledb;
oracledbInst = new oracledbCLib.Oracledb();
extend(oracledbInst);

View File

@ -17,7 +17,10 @@
*
*****************************************************************************/
'use strict';
var connection = require('./connection.js');
var nodbUtil = require('./util.js');
// completeConnectionRequest does the actual work of getting a connection from a
// pool when queuing is enabled. It's abstracted out so it can be called from
@ -119,7 +122,7 @@ function onRequestTimeout(timerIdx) {
self._connRequestQueue.splice(requestIndex, 1);
self._connRequestTimersMap[timerIdx] = null;
payloadToDequeue.getConnectionCb(new Error('NJS-040: connection request timeout'));
payloadToDequeue.getConnectionCb(new Error(nodbUtil.getErrorMessage('NJS-040')));
}
}
@ -134,6 +137,9 @@ function getConnection(getConnectionCb) {
var timeoutHandle;
var timerIdx;
nodbUtil.assert(arguments.length === 1, 'NJS-009');
nodbUtil.assert(typeof getConnectionCb === 'function', 'NJS-006', 1);
// Added this check because if the pool isn't valid and we reference self.poolMax
// (which is a C layer getter) an error will be thrown.
if (!self._isValid) {
@ -150,21 +156,21 @@ function getConnection(getConnectionCb) {
}
if (self.queueRequests === false) { // queueing is disabled for pool
if (self._enableStats) {
self._getConnection(function(err, connInst) {
if (err) {
self._getConnection(function(err, connInst) {
if (err) {
if (self._enableStats) {
self._totalFailedRequests += 1;
getConnectionCb(err);
return;
}
getConnectionCb(null, connInst);
});
} else {
self._getConnection(getConnectionCb);
}
getConnectionCb(err);
return;
}
connection.extend(connInst, self._oracledb, self);
getConnectionCb(null, connInst);
});
} else if (self._connectionsOut < self.poolMax) { // queueing enabled, but not needed
completeConnectionRequest.call(self, getConnectionCb);
} else { // need to queue the request
@ -204,6 +210,9 @@ function getConnection(getConnectionCb) {
function terminate(terminateCb) {
var self = this;
nodbUtil.assert(arguments.length === 1, 'NJS-009');
nodbUtil.assert(typeof terminateCb === 'function', 'NJS-006', 1);
self._terminate(function(err) {
if (!err) {
self._isValid = false;
@ -290,13 +299,23 @@ function extend(pool, poolAttrs, oracledb) {
_oracledb: { // storing a reference to the base instance to avoid circular references with require
value: oracledb
},
queueRequests: {
value: queueRequests, // true will queue requests when conn pool is maxed out
enumerable: true
queueRequests: { // true will queue requests when conn pool is maxed out
enumerable: true,
get: function() {
return queueRequests;
},
set: function() {
throw new Error(nodbUtil.getErrorMessage('NJS-014', 'queueRequests'));
}
},
queueTimeout: {
value: queueTimeout, // milliseconds a connection request can spend in queue before being failed
enumerable: true
queueTimeout: { // milliseconds a connection request can spend in queue before being failed
enumerable: true,
get: function() {
return queueTimeout;
},
set: function() {
throw new Error(nodbUtil.getErrorMessage('NJS-014', 'queueTimeout'));
}
},
_isValid: { // used to ensure operations are not done after terminate
value: true,
@ -373,7 +392,7 @@ function extend(pool, poolAttrs, oracledb) {
value: pool.getConnection
},
getConnection: {
value: getConnection,
value: nodbUtil.promisify(getConnection),
enumerable: true,
writable: true
},
@ -381,7 +400,12 @@ function extend(pool, poolAttrs, oracledb) {
value: pool.terminate
},
terminate: {
value: terminate,
value: nodbUtil.promisify(terminate),
enumerable: true,
writable: true
},
close: { // alias for terminate
value: nodbUtil.promisify(terminate),
enumerable: true,
writable: true
}

226
lib/querystream.js Normal file
View File

@ -0,0 +1,226 @@
/* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. */
/******************************************************************************
*
* You may not use the identified files except in compliance with the Apache
* License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at
* http://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.
*
*****************************************************************************/
'use strict';
var util = require('util');
var Readable = require('stream').Readable;
// This class was originally based on https://github.com/sagiegurari/simple-oracledb/blob/master/lib/resultset-read-stream.js
function QueryStream(resultSet, oracledb) {
var self = this;
Object.defineProperties(
self,
{
_oracledb: { // storing a reference to the base instance to avoid circular references with require
value: oracledb
},
_resultSet: {
value: resultSet,
writable: true
},
_fetchedRows: { // a local cache of rows fetched from a call to resultSet.getRows
value: [],
writable: true
},
_fetchedAllRows: { // used to avoid an unnecessary call to resultSet.getRows
value: false,
writable: true
},
_fetching: { // used to serialize method calls on the resultset
value: false,
writable: true
},
_closed: { // used to track that the stream is closed
value: false,
writable: true
}
}
);
Readable.call(self, {
objectMode: true
});
if (self._resultSet) { // If true, no need to invoke _open, we are ready to go.
self.emit('metadata', self._resultSet.metaData);
self.emit('open');
}
}
util.inherits(QueryStream, Readable);
// The _open method is only meant to be called when a QueryStream is created
// but not passed in the resultSet during initialization. In those cases the
// QueryStream object will have been returned immediately and the _open method
// will be called later to pass the resultset (or error getting the resultset)
// along.
QueryStream.prototype._open = function(err, rs) {
var self = this;
if (err) {
self.emit('error', err);
return;
}
self._resultSet = rs;
self.emit('metadata', self._resultSet.metaData);
// Trigger the event listener that may have been added in _read now that the
// resultset is ready.
self.emit('open');
};
// The stream _read implementation which fetches the next row from the resultset.
QueryStream.prototype._read = function () {
var self = this;
var fetchCount;
if (!self._resultSet) {
// Still waiting on the resultset, add an event listener to retry when ready
return self.once('open', function() {
self._read();
});
}
if (self._closed) {
return;
}
if (self._fetchedRows.length) {
// We have rows already fetched that need to be pushed
self.push(self._fetchedRows.shift());
} else if (self._fetchedAllRows) {
// Calling the C layer close directly to avoid assertions on the public method
self._resultSet._close(function(err) {
if (err) {
self.emit('error', err);
return;
}
// Signal the end of the stream
self.push(null);
});
} else {
// Using _fetching to indicate that the resultset is working to avoid potential
// errors related to close w/conncurrent operations on resultsets
self._fetching = true;
fetchCount = self._oracledb.maxRows || 100;
// Calling the C layer getRows directly to avoid assertions on the public method
self._resultSet._getRows(fetchCount, function(err, rows) {
if (err) {
// We'll return the error from getRows, but first try to close the resultSet.
self._resultSet.close(function() {});
self.emit('error', err);
return;
}
self._fetching = false;
// Close may have been called while the resultset was fetching.
if (self._closed) {
// Trigger the event listener that may have been added in close now that
// the resultset has finished working.
self.emit('_doneFetching');
return;
}
self._fetchedRows = rows;
if (self._fetchedRows.length < fetchCount) {
self._fetchedAllRows = true;
}
if (self._fetchedRows.length) {
self.push(self._fetchedRows.shift());
} else { // No more rows to fetch
// Calling the C layer close directly to avoid assertions on the public method
self._resultSet._close(function(err) {
if (err) {
self.emit('error', err);
return;
}
// Signal the end of the stream
self.push(null);
});
}
});
}
};
// The close method is not a standard method on stream instances in Node.js but
// it was added to provide developers with a means of stopping the flow of data
// and closing the stream without having to allow the entire resultset to finish
// streaming.
function close(callback) {
var self = this;
// Setting _closed early to prevent _read invocations from being processed and
// to allow _doneFetching to be emitted if needed.
self._closed = true;
// We can't close the resultset if it's currently fetching. Add a listener
// to call close when the resulset is done fetching.
if (self._fetching) {
self.once('_doneFetching', function() {
self._close(callback);
});
return;
}
if (callback) {
self.once('close', callback);
}
// It's possible for close to be called very early, even before the resultset
// has been set via _open (if needed).
if (!self._resultSet) {
self.emit('close');
} else {
// Calling the C layer close directly to avoid assertions on the public method
self._resultSet._close(function(err) {
if (err) {
self.emit('error', err);
return;
}
self.emit('close');
});
}
}
// Exposing close as a private method for now.
Object.defineProperty(
QueryStream.prototype,
'_close',
{
value: close,
writable: true
}
);
module.exports = QueryStream;

View File

@ -1,121 +0,0 @@
/* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. */
/******************************************************************************
*
* You may not use the identified files except in compliance with the Apache
* License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at
* http://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.
*
*****************************************************************************/
var util = require('util');
var stream = require('stream');
var Readable = stream.Readable;
var resultset = require('./resultset.js');
// A node.js read stream for resultsets (based on https://github.com/sagiegurari/simple-oracledb/blob/master/lib/resultset-read-stream.js).
function ResultSetReadStream(conn, sql, binding, options) {
var self = this;
binding = binding || [];
options = options || {};
options.resultSet = true;
self.streamNumRows = conn._oracledb.maxRows || 100;
Readable.call(self, {
objectMode: true
});
Object.defineProperty(self, 'nextRow', {
// Sets the nextRow value.
set: function (nextRow) {
self.next = nextRow;
if (self.inRead) {
self._read();
}
}
});
conn._execute(sql, binding, options, function(err, result) {
self._onExecuteDone(err, result);
});
}
util.inherits(ResultSetReadStream, Readable);
// The stream _read implementation which fetches the next row from the resultset.
ResultSetReadStream.prototype._read = function () {
var self = this;
self.inRead = false;
if (self.next) {
self.next(function onNextRowRead(error, data) {
if (error) {
self.emit('error', error);
} else if (data) {
self.push(data);
} else {
self.push(null);
}
});
} else {
self.inRead = true;
}
};
ResultSetReadStream.prototype._onExecuteDone = function(err, result) {
var self = this;
if (err) {
self.nextRow = function emitError(streamCallback) {
streamCallback(err);
};
return;
}
resultset.extend(result.resultSet);
self.emit('metadata', result.resultSet.metaData);
var close = function (streamCallback, causeError) {
result.resultSet.close(function onClose(closeError) {
streamCallback(causeError || closeError);
});
};
var readRows;
self.nextRow = function fetchNextRow(streamCallback) {
if (readRows && readRows.length) {
streamCallback(null, readRows.shift());
} else {
result.resultSet.getRows(self.streamNumRows, function onRow(rowError, rows) {
if (rowError) {
close(streamCallback, rowError);
} else if ((!rows) || (!rows.length)) {
close(streamCallback);
} else {
readRows = rows;
streamCallback(null, readRows.shift());
}
});
}
};
};
module.exports = ResultSetReadStream;

View File

@ -17,41 +17,117 @@
*
*****************************************************************************/
'use strict';
var QueryStream = require('./querystream.js');
var nodbUtil = require('./util.js');
// This close function is just a place holder to allow for easier extension later.
function close() {
function close(closeCb) {
var self = this;
self._close.apply(self, arguments);
nodbUtil.assert(arguments.length === 1, 'NJS-009');
nodbUtil.assert(typeof closeCb === 'function', 'NJS-006', 1);
if (self._convertedToStream) {
closeCb(new Error(nodbUtil.getErrorMessage('NJS-042')));
return;
}
self._processingStarted = true;
self._close(function(err) {
// Need to maintain a reference to the resultset instance to ensure that the
// garbage collector doesn't destroy it too soon.
self = undefined;
closeCb(err);
});
}
// This getRow function is just a place holder to allow for easier extension later.
function getRow() {
function getRow(getRowCb) {
var self = this;
nodbUtil.assert(arguments.length === 1, 'NJS-009');
nodbUtil.assert(typeof getRowCb === 'function', 'NJS-006', 1);
if (self._convertedToStream) {
getRowCb(new Error(nodbUtil.getErrorMessage('NJS-042')));
return;
}
self._processingStarted = true;
self._getRow.apply(self, arguments);
}
// This getRows function is just a place holder to allow for easier extension later.
function getRows() {
function getRows(numRows, getRowsCb) {
var self = this;
nodbUtil.assert(arguments.length === 2, 'NJS-009');
nodbUtil.assert(typeof numRows === 'number', 'NJS-006', 1);
nodbUtil.assert(typeof getRowsCb === 'function', 'NJS-006', 2);
if (self._convertedToStream) {
getRowsCb(new Error(nodbUtil.getErrorMessage('NJS-042')));
return;
}
self._processingStarted = true;
self._getRows.apply(self, arguments);
}
function toQueryStream() {
var self = this;
var stream;
nodbUtil.assert(arguments.length === 0, 'NJS-009');
if (self._processingStarted) {
throw new Error(nodbUtil.getErrorMessage('NJS-041'));
return;
}
if (self._convertedToStream) {
throw new Error(nodbUtil.getErrorMessage('NJS-043'));
return;
}
self._convertedToStream = true;
stream = new QueryStream(self, self._oracledb);
return stream;
}
// The extend method is used to extend the ResultSet instance from the C layer with
// custom properties and method overrides. References to the original methods are
// maintained so they can be invoked by the overriding method at the right time.
function extend(resultSet) {
function extend(resultSet, oracledb) {
// Using Object.defineProperties to add properties to the ResultSet instance with
// special properties, such as enumerable but not writable.
Object.defineProperties(
resultSet,
{
_oracledb: { // storing a reference to the base instance to avoid circular references with require
value: oracledb
},
_processingStarted: { // used to prevent conversion to stream after invoking methods
value: false,
writable: true
},
_convertedToStream: { // used to prevent invoking methods after conversion to stream
value: false,
writable: true
},
_close: {
value: resultSet.close
},
close: {
value: close,
value: nodbUtil.promisify(close),
enumerable: true,
writable: true
},
@ -59,7 +135,7 @@ function extend(resultSet) {
value: resultSet.getRow
},
getRow: {
value: getRow,
value: nodbUtil.promisify(getRow),
enumerable: true,
writable: true
},
@ -67,7 +143,12 @@ function extend(resultSet) {
value: resultSet.getRows
},
getRows: {
value: getRows,
value: nodbUtil.promisify(getRows),
enumerable: true,
writable: true
},
toQueryStream: {
value: toQueryStream,
enumerable: true,
writable: true
}

122
lib/util.js Normal file
View File

@ -0,0 +1,122 @@
/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. */
/******************************************************************************
*
* You may not use the identified files except in compliance with the Apache
* License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at
* http://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.
*
*****************************************************************************/
var util = require('util');
// errorMessages is a temporary duplication of error messages defined in the C
// layer that will be removed once a function to fetch from the C layer is added.
var errorMessages = {
'NJS-005': 'NJS-005: invalid value for parameter %d',
'NJS-006': 'NJS-006: invalid type for parameter %d',
'NJS-009': 'NJS-009: invalid number of parameters',
'NJS-014': 'NJS-014: %s is a read-only property',
'NJS-037': 'NJS-037: incompatible type of value provided',
'NJS-040': 'NJS-040: connection request timeout',
'NJS-041': 'NJS-041: cannot convert ResultSet to QueryStream after invoking methods',
'NJS-042': 'NJS-042: cannot invoke ResultSet methods after converting to QueryStream',
'NJS-043': "NJS-043: ResultSet already converted to QueryStream"
};
// getErrorMessage is used to get and format error messages to make throwing errors
// a little more convenient.
function getErrorMessage(errorCode, messageArg1) {
if (messageArg1) {
return util.format(errorMessages[errorCode], messageArg1);
} else {
return util.format(errorMessages[errorCode]);
}
}
module.exports.getErrorMessage = getErrorMessage;
// assert it typically used in the beginning of public functions to assert preconditions
// for the function to execute. Most commonly it's used to validate arguments lenght
// and types and throw an error if they don't match what is expected.
function assert(condition, errorCode, messageArg1) {
if (!condition) {
throw new Error(getErrorMessage(errorCode, messageArg1));
}
}
module.exports.assert = assert;
// The promisify function is used to wrap async methods to add optional promise
// support. If the last parameter passed to a method is a function, then it is
// assumed that the callback pattern is being used and the method is invoked as
// usual. Otherwise a promise is returned and later resolved or rejected based on
// the return of the method.
function promisify(func) {
return function() {
var self = this;
var args;
// This/self could refer to the base class instance, pool, connection, etc. All
// class instances have a private reference to the base class for convenience.
if (!self._oracledb.Promise || typeof arguments[arguments.length - 1] === 'function') {
return func.apply(self, arguments);
} else {
// Converting to an array so we can extend it later with a custom callback
args = Array.prototype.slice.call(arguments);
return new self._oracledb.Promise(function(resolve, reject) {
var errorCode;
try {
args[args.length] = function(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
};
func.apply(self, args);
} catch (err) {
errorCode = err.message.substr(0, 7);
// Check for invalid number or type of parameter(s) as they should be
// eagerly thrown.
if (errorCode === 'NJS-009' || errorCode === 'NJS-006') {
// Throwing the error outside of the promise wrapper so that its not
// swallowed up as a rejection.
process.nextTick(function() {
throw err;
});
} else {
reject(err);
}
}
});
}
};
};
module.exports.promisify = promisify;
function isObject(value) {
return value !== null && typeof value === 'object';
}
module.exports.isObject = isObject;
function isObjectOrArray(value) {
return (value !== null && typeof value === 'object') || Array.isArray(value);
}
module.exports.isObjectOrArray = isObjectOrArray;

View File

@ -1,6 +1,6 @@
{
"name": "oracledb",
"version": "1.8.0",
"version": "1.9.0",
"description": "Oracle Database driver by Oracle Corp.",
"license": "Apache-2.0",
"homepage": "http://www.oracle.com/technetwork/database/database-technologies/scripting-languages/node_js/",

View File

@ -51,9 +51,9 @@ static const char *errMsg[] =
"NJS-013: invalid bind direction",
"NJS-014: %s is a read-only property",
"NJS-016: buffer is too small for OUT binds",
"NJS-017: concurrent operations on resultSet are not allowed",
"NJS-017: concurrent operations on ResultSet are not allowed",
"NJS-018: invalid result set",
"NJS-019: resultSet cannot be returned for non-query statements",
"NJS-019: ResultSet cannot be returned for non-query statements",
"NJS-020: empty array was specified to fetch values as string",
"NJS-021: invalid type for conversion specified",
"NJS-022: invalid LOB",
@ -63,13 +63,10 @@ static const char *errMsg[] =
"NJS-026: maxRows must be greater than zero",
"NJS-027: unexpected SQL parsing error",
"NJS-028: raw database type is not supported with DML Returning statements",
"NJS-029: Invalid object from javascript",
"NJS-030: connection cannot be released because Lob operations are in"
" progress",
"NJS-031: connection cannot be released because ResultSet operations are"
" in progress",
"NJS-032: connection cannot be released because a database call is in"
" progress",
"NJS-029: invalid object from JavaScript",
"NJS-030: connection cannot be released because Lob operations are in progress",
"NJS-031: connection cannot be released because ResultSet operations are in progress",
"NJS-032: connection cannot be released because a database call is in progress",
"NJS-033: an internal error occurred. [%s][%s]",
"NJS-034: data type is unsupported for array bind",
"NJS-035: maxArraySize is required for IN OUT array bind",
@ -78,6 +75,9 @@ static const char *errMsg[] =
"NJS-038: maxArraySize value should be greater than 0",
"NJS-039: empty array is not allowed for IN bind",
"NJS-040: connection request timeout",
"NJS-041: cannot convert ResultSet to QueryStream after invoking methods",
"NJS-042: cannot invoke ResultSet methods after converting to QueryStream",
"NJS-043: ResultSet already converted to QueryStream",
};
string NJSMessages::getErrorMsg ( NJSErrorType err, ... )

View File

@ -74,6 +74,9 @@ typedef enum
errInvalidValueArrayBind,
errEmptyArray,
errConnRequestTimeout,
errCannotConvertRsToStream,
errCannotInvokeRsMethods,
errResultSetAlreadyConverted,
// New ones should be added here

View File

@ -69,7 +69,7 @@ using namespace v8;
/* Keep the version in sync with package.json */
#define NJS_NODE_ORACLEDB_MAJOR 1
#define NJS_NODE_ORACLEDB_MINOR 8
#define NJS_NODE_ORACLEDB_MINOR 9
#define NJS_NODE_ORACLEDB_PATCH 0
/* Used for Oracledb.version */

View File

@ -718,7 +718,6 @@ describe('1. connection.js', function(){
describe('1.6 Testing parameter assertions', function() {
var conn1;
var sql = 'select 1 from dual';
beforeEach('get connection ready', function(done) {
oracledb.getConnection(credential, function(err, conn) {
@ -735,37 +734,173 @@ describe('1. connection.js', function(){
});
});
it('1.6.1 too few params without a callback should throw error', function(done) {
it('1.6.1 too few params should throw an error', function(done) {
// This test returns a promise because the last parameter to execute is not
// a function. Normally, errors thrown in a promise would be directed to
// to a catch handler. In the case of an "accidental promise" the error
// could go undetected. Because of this, the promisify function in util.js
// uses process.nextTick to throw invalid number or type of params (NJS-009
// and NJS-006). This test has been updated to account for this behavior.
var promiseSupportEnabled = oracledb.Promise !== undefined;
var listeners = process.listeners('uncaughtException');
if (promiseSupportEnabled) {
process.removeAllListeners('uncaughtException');
process.once('uncaughtException', function(err) {
listeners.forEach(function(listener) {
process.on('uncaughtException', listener);
});
should.exist(err);
done();
});
}
// Using try catch for instances where promises are not supported or have
// been disabled by setting oracledb.Promise to something falsey.
try {
conn1.execute(sql);
conn1.execute();
} catch (err) {
if (promiseSupportEnabled) {
listeners.forEach(function(listener) {
process.on('uncaughtException', listener);
});
}
should.exist(err);
done();
}
});
it('1.6.2 too few params with a callback should pass error in callback', function(done) {
conn1.execute(function(err, result) {
should.exist(err);
done();
});
});
it('1.6.2 too many params should throw error', function(done) {
// This test returns a promise because the last parameter to execute is not
// a function. Normally, errors thrown in a promise would be directed to
// to a catch handler. In the case of an "accidental promise" the error
// could go undetected. Because of this, the promisify function in util.js
// uses process.nextTick to throw invalid number or type of params (NJS-009
// and NJS-006). This test has been updated to account for this behavior.
var promiseSupportEnabled = oracledb.Promise !== undefined;
var listeners = process.listeners('uncaughtException');
it('1.6.3 too many params without a callback should throw error', function(done) {
if (promiseSupportEnabled) {
process.removeAllListeners('uncaughtException');
process.once('uncaughtException', function(err) {
listeners.forEach(function(listener) {
process.on('uncaughtException', listener);
});
should.exist(err);
done();
});
}
// Using try catch for instances where promises are not supported or have
// been disabled by setting oracledb.Promise to something falsey.
try {
conn1.execute(1, 2, 3, 4, 5);
} catch (err) {
if (promiseSupportEnabled) {
listeners.forEach(function(listener) {
process.on('uncaughtException', listener);
});
}
should.exist(err);
done();
}
});
it('1.6.3 wrong type for param 1 should throw an error', function(done) {
// Don't need to listen for unhandledRejection because a promise will not
// be returned as the last param is a function.
try {
conn1.execute(1, function() {});
} catch (err) {
should.exist(err);
done();
}
});
it('1.6.4 too many params with a callback should pass error in callback', function(done) {
conn1.execute(1, 2, 3, 4, function(err, result) {
it('1.6.4 wrong type for param 2 should throw an error', function(done) {
// This test returns a promise because the last parameter to execute is not
// a function. Normally, errors thrown in a promise would be directed to
// to a catch handler. In the case of an "accidental promise" the error
// could go undetected. Because of this, the promisify function in util.js
// uses process.nextTick to throw invalid number or type of params (NJS-009
// and NJS-006). This test has been updated to account for this behavior.
var promiseSupportEnabled = oracledb.Promise !== undefined;
var listeners = process.listeners('uncaughtException');
if (promiseSupportEnabled) {
process.removeAllListeners('uncaughtException');
process.once('uncaughtException', function(err) {
listeners.forEach(function(listener) {
process.on('uncaughtException', listener);
});
should.exist(err);
done();
});
}
// Using try catch for instances where promises are not supported or have
// been disabled by setting oracledb.Promise to something falsey.
try {
conn1.execute('select 1 from dual', 1);
} catch (err) {
if (promiseSupportEnabled) {
listeners.forEach(function(listener) {
process.on('uncaughtException', listener);
});
}
should.exist(err);
done();
}
});
it('1.6.5 wrong type for param 3 should throw an error', function(done) {
// Don't need to listen for unhandledRejection because a promise will not
// be returned as the last param is a function.
try {
conn1.execute('select 1 from dual', 1, function() {});
} catch (err) {
should.exist(err);
done();
}
});
it('1.6.6 wrong type for param 4 should throw an error', function(done) {
// Don't need to listen for unhandledRejection because a promise will not
// be returned as the last param is a function.
try {
conn1.execute('select 1 from dual', {}, 1, function() {});
} catch (err) {
should.exist(err);
done();
}
});
});
describe('1.7 Close method', function() {
it('1.7.1 close can be used as an alternative to release', function(done) {
oracledb.getConnection(credential, function(err, conn) {
should.not.exist(err);
conn.close(function(err) {
should.not.exist(err);
done();
});
});
});
})
});
})
});

View File

@ -18,6 +18,15 @@
1.5 Testing commit() & rollback() functions
1.5.1 commit() function works well
1.5.2 rollback() function works well
1.6 Testing parameter assertions
1.6.1 too few params should throw an error
1.6.2 too many params should throw error
1.6.3 wrong type for param 1 should throw an error
1.6.4 wrong type for param 2 should throw an error
1.6.5 wrong type for param 3 should throw an error
1.6.6 wrong type for param 4 should throw an error
1.7 Close method
1.7.1 close can be used as an alternative to release
2. pool.js
2.1 default values
@ -57,6 +66,8 @@
2.8.4 does not generate NJS-040 if request is queued for less time than queueTimeout
2.9 connection request queue (_enableStats & _logStats functionality)_logStats must be called prior to terminating pool.
2.9.1 works after the pool as been terminated
2.10 Close method
2.10.1 close can be used as an alternative to release
3. examples.js
@ -227,7 +238,7 @@
12.7.2 maxRows option is ignored with REF Cursor
13. stream.js
13.1 Testing ResultSet stream
13.1 Testing QueryStream
13.1.1 stream results for oracle connection
13.1.2 stream results for oracle connection (outFormat: oracledb.OBJECT)
13.1.3 errors in query
@ -239,6 +250,13 @@
13.1.9 Read CLOBs after stream close
13.1.10 meta data
13.1.11 stream stress test
13.2 Testing QueryStream._close
13.2.1 should be able to stop the stream early with _close
13.2.2 should be able to stop the stream before any data
13.2.3 should invoke an optional callback passed to _close
13.3 Testing QueryStream\'s maxRows control
13.3.1 should use oracledb.maxRows for fetching
13.3.2 should default to 100 if oracledb.maxRows is falsey
14. stream2.js
14.1 Bind by position and return an array
@ -249,7 +267,32 @@
14.6 maxRows option is ignored as expect
14.7 Negative - queryStream() has no parameters
14.8 Negative - give invalid SQL as first parameter
14.9 Negatvie - give non-query SQL
14.9 Negative - give non-query SQL
15. resultsetToQueryStream.js
15.1 Testing ResultSet.toQueryStream
15.1.1 should allow resultsets to be converted to streams
15.2 Testing ResultSet/QueryStream conversion errors
15.2.1 should prevent conversion to stream after getRow is invoked
15.2.2 should prevent conversion to stream after getRows is invoked
15.2.3 should prevent conversion to stream after close is invoked
15.2.4 should prevent invoking getRow after conversion to stream
15.2.5 should prevent invoking getRows after conversion to stream
15.2.6 should prevent invoking close after conversion to stream
15.2.7 should prevent calling toQueryStream more than once
16. promises.js
16.1 returns a promise from oracledb.getConnection
16.2 returns a promise from oracledb.createPool
16.3 returns a promise from pool.terminate
16.4 returns a promise from pool.getConnection
16.5 returns a promise from connection.release
16.6 returns a promise from connection.execute
16.7 returns a promise from connection.commit
16.8 returns a promise form connection.rollback
16.9 returns a promise from resultSet.close
16.10 returns a promise from resultSet.getRow
16.11 returns a promise from resultSet.getRows
21. datatypeAssist.js

View File

@ -640,12 +640,14 @@ describe('2. pool.js', function(){
);
});
it('2.7.1 throws error if called after pool is terminated and a callback is not provided', function(done) {
// Skipping this test because assertions were added to the JS layer for all
// public methods. This now throws NJS-009: invalid number of parameters.
it.skip('2.7.1 throws error if called after pool is terminated and a callback is not provided', function(done) {
pool1.terminate(function(err) {
should.not.exist(err);
try {
pool1.getConnection(1);
pool1.getConnection();
} catch (err) {
should.exist(err);
(err.message).should.startWith('NJS-002: invalid pool');
@ -953,4 +955,30 @@ describe('2. pool.js', function(){
);
});
});
})
describe('2.10 Close method', function(){
it('2.10.1 close can be used as an alternative to release', function(done) {
oracledb.createPool(
{
externalAuth : credential.externalAuth,
user : credential.user,
password : credential.password,
connectString : credential.connectString,
poolMin : 0,
poolMax : 1,
poolIncrement : 1,
poolTimeout : 1
},
function(err, pool){
should.not.exist(err);
pool.close(function(err) {
should.not.exist(err);
done();
});
}
);
});
});
});

339
test/promises.js Normal file
View File

@ -0,0 +1,339 @@
/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. */
/******************************************************************************
*
* You may not use the identified files except in compliance with the Apache
* License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at
* http://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.
*
* The node-oracledb test suite uses 'mocha', 'should' and 'async'.
* See LICENSE.md for relevant licenses.
*
* NAME
* 16. promises.js
*
* DESCRIPTION
* Promise tests.
*
* NUMBERING RULE
* Test numbers follow this numbering rule:
* 1 - 20 are reserved for basic functional tests
* 21 - 50 are reserved for data type supporting tests
* 51 onwards are for other tests
*
*****************************************************************************/
'use strict';
var oracledb = require('oracledb');
var should = require('should');
var async = require('async');
var dbConfig = require('./dbconfig.js');
// Need to skip these tests if Promises are not supported
var it = (oracledb.Promise) ? global.it : global.it.skip;
describe('16. promises.js', function(){
it('16.1 returns a promise from oracledb.getConnection', function(done) {
var promise = oracledb.getConnection(dbConfig);
promise.should.be.an.instanceof(oracledb.Promise);
promise
.then(function(conn) {
conn.should.be.ok;
conn.release(function(err) {
if (err)
return done(err);
else
return done();
});
})
.catch(function(err) {
return done(err);
});
})
it('16.2 returns a promise from oracledb.createPool', function(done) {
var promise = oracledb.createPool(dbConfig);
promise.should.be.an.instanceof(oracledb.Promise);
promise
.then(function(pool) {
pool.should.be.ok;
pool.terminate(function(err) {
if (err)
return done(err);
else
return done();
});
})
.catch(function(err) {
return done(err);
});
})
it('16.3 returns a promise from pool.terminate', function(done) {
oracledb.createPool(dbConfig)
.then(function(pool) {
pool.should.be.ok;
var promise = pool.terminate();
promise.should.be.an.instanceof(oracledb.Promise);
return promise;
})
.then(function() {
return done();
})
.catch(function(err) {
return done(err);
});
})
it('16.4 returns a promise from pool.getConnection', function(done) {
oracledb.createPool(dbConfig)
.then(function(pool) {
pool.should.be.ok;
var getConnPromise = pool.getConnection();
getConnPromise.should.be.an.instanceof(oracledb.Promise);
getConnPromise
.then(function(conn) {
conn.release(function(err) {
if (err) {
return done(err);
}
pool.terminate()
.then(function() {
return done();
})
.catch(function(err) {
return done(err);
});
});
});
})
.catch(function(err) {
return done(err);
});
})
it('16.5 returns a promise from connection.release', function(done) {
oracledb.getConnection(dbConfig)
.then(function(conn) {
conn.should.be.ok;
var promise = conn.release();
promise.should.be.an.instanceof(oracledb.Promise);
return promise;
})
.then(function() {
return done();
})
.catch(function(err) {
return done(err);
});
})
it('16.6 returns a promise from connection.execute', function(done) {
oracledb.getConnection(dbConfig)
.then(function(conn) {
conn.should.be.ok;
var executePromise = conn.execute('select 1 from dual');
executePromise.should.be.an.instanceof(oracledb.Promise);
return executePromise
.then(function(result) {
result.rows[0][0].should.eql(1);
return conn.release()
.then(done);
});
})
.catch(function(err) {
return done(err);
});
})
it('16.7 returns a promise from connection.commit', function(done) {
oracledb.getConnection(dbConfig)
.then(function(conn) {
var commitPromise;
conn.should.be.ok;
commitPromise = conn.commit();
commitPromise.should.be.an.instanceof(oracledb.Promise);
return commitPromise
.then(function() {
return conn.release()
.then(done);
});
})
.catch(function(err) {
return done(err);
});
})
it('16.8 returns a promise form connection.rollback', function(done) {
oracledb.getConnection(dbConfig)
.then(function(conn) {
var rollbackPromise;
conn.should.be.ok;
rollbackPromise = conn.rollback();
rollbackPromise.should.be.an.instanceof(oracledb.Promise);
return rollbackPromise
.then(function() {
return conn.release()
.then(done);
});
})
.catch(function(err) {
return done(err);
});
})
it('16.9 returns a promise from resultSet.close', function(done) {
oracledb.getConnection(dbConfig)
.then(function(conn) {
conn.should.be.ok;
return conn.execute('select 1 from dual', [], {resultSet: true})
.then(function(result) {
var closePromise;
closePromise = result.resultSet.close();
closePromise.should.be.an.instanceof(oracledb.Promise);
return closePromise
.then(function() {
return conn.release()
.then(done);
});
});
})
.catch(function(err) {
return done(err);
});
})
it('16.10 returns a promise from resultSet.getRow', function(done) {
function finishProcessing(conn, resultSet) {
return resultSet.close()
.then(function() {
conn.release();
})
}
function processResultSet(conn, resultSet) {
return new Promise(function(resolve, reject) {
function processRow() {
var getRowPromise;
getRowPromise = resultSet.getRow();
getRowPromise.should.be.an.instanceof(oracledb.Promise);
getRowPromise
.then(function(row) {
if (!row) {
finishProcessing(conn, resultSet)
.then(function() {
resolve();
});
} else {
row[0].should.eql(1);
processRow();
}
})
.catch(function(err) {
reject(err);
});
}
processRow();
});
}
oracledb.getConnection(dbConfig)
.then(function(conn) {
conn.should.be.ok;
return conn.execute('select 1 from dual', [], {resultSet: true})
.then(function(result) {
return processResultSet(conn, result.resultSet)
.then(function() {
done();
});
});
})
.catch(function(err) {
return done(err);
});
}) // 16.10
it('16.11 returns a promise from resultSet.getRows', function(done) {
function finishProcessing(conn, resultSet) {
return resultSet.close()
.then(function() {
conn.release();
});
}
function processResultSet(conn, resultSet) {
return new Promise(function(resolve, reject) {
function processRows() {
var getRowsPromise;
getRowsPromise = resultSet.getRows(2);
getRowsPromise.should.be.an.instanceof(oracledb.Promise);
getRowsPromise
.then(function(rows) {
if (rows.length === 0) {
finishProcessing(conn, resultSet)
.then(function() {
resolve();
});
} else {
rows[0][0].should.eql(1);
rows[1][0].should.eql(2);
processRows();
}
})
.catch(function(err) {
reject(err);
});
}
processRows();
});
}
oracledb.getConnection(dbConfig)
.then(function(conn) {
conn.should.be.ok;
return conn.execute('select 1 from dual union select 2 from dual', [], {resultSet: true})
.then(function(result) {
return processResultSet(conn, result.resultSet)
.then(function() {
return done();
});
});
})
.catch(function(err) {
return done(err);
});
}) // 16.11
})

View File

@ -630,15 +630,16 @@ describe('12. resultSet1.js', function() {
);
function fetchRowFromRS(rs) {
rs.getRows(function(err, rows) {
try {
rs.getRows(function() {});
} catch (err) {
should.exist(err);
err.message.should.eql('NJS-009: invalid number of parameters');
should.not.exist(rows);
rs.close(function(err) {
should.not.exist(err);
done();
});
});
}
}
})
@ -685,7 +686,7 @@ describe('12. resultSet1.js', function() {
function fetchRowFromRS(rs, numRows) {
rs.getRows(numRows, function(err, rows) {
should.exist(err);
err.message.should.startWith('NJS-006: invalid type for parameter 1');
err.message.should.eql('NJS-006: invalid type for parameter 1');
rs.close(function(err) {
should.not.exist(err);
done();
@ -709,14 +710,16 @@ describe('12. resultSet1.js', function() {
);
function fetchRowFromRS(rs, numRows) {
rs.getRows(numRows, function(err, rows) {
try {
rs.getRows(numRows, function() {});
} catch (err) {
should.exist(err);
err.message.should.startWith('NJS-006: invalid type for parameter 1');
rs.close(function(err) {
should.not.exist(err);
done();
});
});
}
}
})
})
@ -870,15 +873,16 @@ describe('12. resultSet1.js', function() {
);
function fetchRowFromRS(rs, numRows) {
rs.getRow(numRows, function(err, row) {
try {
rs.getRow(numRows, function() {});
} catch (err) {
should.exist(err);
err.message.should.eql('NJS-009: invalid number of parameters');
should.not.exist(row);
rs.close(function(err) {
should.not.exist(err);
done();
});
});
}
}
})

View File

@ -0,0 +1,377 @@
/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. */
/******************************************************************************
*
* You may not use the identified files except in compliance with the Apache
* License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at
* http://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.
*
* The node-oracledb test suite uses 'mocha', 'should' and 'async'.
* See LICENSE.md for relevant licenses.
*
* NAME
* 15. resultsetToStream.js
*
* DESCRIPTION
* Testing driver query results via stream feature.
*
* NUMBERING RULE
* Test numbers follow this numbering rule:
* 1 - 20 are reserved for basic functional tests
* 21 - 50 are reserved for data type supporting tests
* 51 onwards are for other tests
*
*****************************************************************************/
'use strict';
var oracledb = require('oracledb');
var should = require('should');
var async = require('async');
var dbConfig = require('./dbconfig.js');
describe('15. resultsetToStream.js', function () {
if (dbConfig.externalAuth) {
var credential = {externalAuth: true, connectString: dbConfig.connectString};
} else {
var credential = dbConfig;
}
var connection = null;
var rowsAmount = 217;
before(function(done) {
async.series([
function getConn(cb) {
oracledb.getConnection(credential, function(err, conn) {
should.not.exist(err);
connection = conn;
cb();
});
},
function createTab(cb) {
var proc = "BEGIN \n" +
" DECLARE \n" +
" e_table_exists EXCEPTION; \n" +
" PRAGMA EXCEPTION_INIT(e_table_exists, -00942);\n " +
" BEGIN \n" +
" EXECUTE IMMEDIATE ('DROP TABLE nodb_employees'); \n" +
" EXCEPTION \n" +
" WHEN e_table_exists \n" +
" THEN NULL; \n" +
" END; \n" +
" EXECUTE IMMEDIATE (' \n" +
" CREATE TABLE nodb_employees ( \n" +
" employees_id NUMBER, \n" +
" employees_name VARCHAR2(20), \n" +
" employees_history CLOB \n" +
" ) \n" +
" '); \n" +
"END; ";
connection.execute(
proc,
function(err) {
should.not.exist(err);
cb();
}
);
},
function insertRows(cb) {
var proc = "DECLARE \n" +
" x NUMBER := 0; \n" +
" n VARCHAR2(20); \n" +
" clobData CLOB; \n" +
"BEGIN \n" +
" FOR i IN 1..217 LOOP \n" +
" x := x + 1; \n" +
" n := 'staff ' || x; \n" +
" INSERT INTO nodb_employees VALUES (x, n, EMPTY_CLOB()) RETURNING employees_history INTO clobData; \n" +
" DBMS_LOB.WRITE(clobData, 20, 1, '12345678901234567890'); \n" +
" END LOOP; \n" +
"end; ";
connection.execute(
proc,
function(err) {
should.not.exist(err);
cb();
}
);
}
], done);
}) // before
after(function(done) {
async.series([
function(callback) {
connection.execute(
"DROP TABLE nodb_employees",
function(err) {
should.not.exist(err);
callback();
}
);
},
function(callback) {
connection.release(function(err) {
should.not.exist(err);
callback();
});
},
], done);
}) // after
describe('15.1 Testing ResultSet.toQueryStream', function () {
it('15.1.1 should allow resultsets to be converted to streams', function (done) {
connection.execute(
'begin \n' +
' open :cursor for select employees_name from nodb_employees; \n' +
'end;',
{
cursor: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
function(err, result) {
should.not.exist(err);
var stream = result.outBinds.cursor.toQueryStream();
stream.on('error', function (error) {
console.log(error);
should.fail(error, null, 'Error event should not be triggered');
});
var counter = 0;
stream.on('data', function (data) {
should.exist(data);
counter++;
});
stream.on('end', function () {
should.equal(counter, rowsAmount);
setTimeout(done, 500);
});
}
);
});
});
describe('15.2 Testing ResultSet/QueryStream conversion errors', function () {
it('15.2.1 should prevent conversion to stream after getRow is invoked', function (done) {
connection.execute(
'begin \n' +
' open :cursor for select employees_name from nodb_employees; \n' +
'end;',
{
cursor: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
function(err, result) {
should.not.exist(err);
var cursor = result.outBinds.cursor;
cursor.getRow(function(err, row) {
should.not.exist(err);
cursor.close(function(err) {
should.not.exist(err);
done();
});
});
try {
var stream = cursor.toQueryStream();
} catch (err) {
(err.message).should.startWith('NJS-041:');
// NJS-041: cannot convert to stream after invoking methods
}
}
);
});
it('15.2.2 should prevent conversion to stream after getRows is invoked', function (done) {
connection.execute(
'begin \n' +
' open :cursor for select employees_name from nodb_employees; \n' +
'end;',
{
cursor: { type: oracledb.CURSOR, dir : oracledb.BIND_OUT }
},
function(err, result) {
should.not.exist(err);
var cursor = result.outBinds.cursor;
cursor.getRows(5, function(err, rows) {
should.not.exist(err);
cursor.close(function(err) {
should.not.exist(err);
done();
});
});
try {
var stream = cursor.toQueryStream();
} catch (err) {
(err.message).should.startWith('NJS-041:');
}
}
);
});
it('15.2.3 should prevent conversion to stream after close is invoked', function (done) {
connection.execute(
'begin \n' +
' open :cursor for select employees_name from nodb_employees; \n' +
'end;',
{
cursor: { type: oracledb.CURSOR, dir : oracledb.BIND_OUT }
},
function(err, result) {
should.not.exist(err);
var cursor = result.outBinds.cursor;
cursor.close(function(err) {
should.not.exist(err);
done();
});
try {
var stream = cursor.toQueryStream();
} catch (err) {
(err.message).should.startWith('NJS-041:');
}
}
);
});
it('15.2.4 should prevent invoking getRow after conversion to stream', function (done) {
connection.execute(
'begin \n' +
' open :cursor for select employees_name from nodb_employees; \n' +
'end;',
{
cursor: { type: oracledb.CURSOR, dir : oracledb.BIND_OUT }
},
function(err, result) {
should.not.exist(err);
var cursor = result.outBinds.cursor;
var stream = cursor.toQueryStream();
cursor.getRow(function(err, row) {
(err.message).should.startWith('NJS-042:');
// NJS-042: cannot invoke methods after converting to stream
// Closing cursor via stream._close because the cursor.close method
// is not invokable after conversion to stream.
stream._close(function(err) {
should.not.exist(err);
done();
});
});
}
);
});
it('15.2.5 should prevent invoking getRows after conversion to stream', function (done) {
connection.execute(
'begin \n' +
' open :cursor for select employees_name from nodb_employees; \n' +
'end;',
{
cursor: { type: oracledb.CURSOR, dir : oracledb.BIND_OUT }
},
function(err, result) {
should.not.exist(err);
var cursor = result.outBinds.cursor;
var stream = cursor.toQueryStream();
cursor.getRows(5, function(err, rows) {
(err.message).should.startWith('NJS-042:');
// Closing cursor via stream._close because the cursor.close method
// is not invokable after conversion to stream.
stream._close(function(err) {
should.not.exist(err);
done();
});
});
}
);
});
it('15.2.6 should prevent invoking close after conversion to stream', function (done) {
connection.execute(
'begin \n' +
' open :cursor for select employees_name from nodb_employees; \n' +
'end;',
{
cursor: { type: oracledb.CURSOR, dir : oracledb.BIND_OUT }
},
function(err, result) {
should.not.exist(err);
var cursor = result.outBinds.cursor;
var stream = cursor.toQueryStream();
cursor.close(function(err) {
(err.message).should.startWith('NJS-042:');
// Closing cursor via stream._close because the cursor.close method
// is not invokable after conversion to stream.
stream._close(function(err) {
should.not.exist(err);
done();
});
});
}
);
});
it('15.2.7 should prevent calling toQueryStream more than once', function (done) {
connection.execute(
'begin \n' +
' open :cursor for select employees_name from nodb_employees; \n' +
'end;',
{
cursor: { type: oracledb.CURSOR, dir : oracledb.BIND_OUT }
},
function(err, result) {
should.not.exist(err);
var cursor = result.outBinds.cursor;
// First conversion to stream
var stream = cursor.toQueryStream();
try {
// Second conversion to stream
stream = cursor.toQueryStream();
} catch (err) {
(err.message).should.startWith('NJS-043:');
stream._close(function(err) {
should.not.exist(err);
done();
});
}
}
);
}); // 15.2.7
}); // 15.2
});

View File

@ -39,7 +39,6 @@ var async = require('async');
var dbConfig = require('./dbconfig.js');
describe('13. stream1.js', function () {
var connection = false;
if (dbConfig.externalAuth) {
var credential = {externalAuth: true, connectString: dbConfig.connectString};
@ -47,85 +46,91 @@ describe('13. stream1.js', function () {
var credential = dbConfig;
}
var createTable =
"BEGIN \
DECLARE \
e_table_exists EXCEPTION; \
PRAGMA EXCEPTION_INIT(e_table_exists, -00942); \
BEGIN \
EXECUTE IMMEDIATE ('DROP TABLE nodb_employees'); \
EXCEPTION \
WHEN e_table_exists \
THEN NULL; \
END; \
EXECUTE IMMEDIATE (' \
CREATE TABLE nodb_employees ( \
employees_id NUMBER, \
employees_name VARCHAR2(20), \
employees_history CLOB \
) \
'); \
END; ";
var insertRows =
"DECLARE \
x NUMBER := 0; \
n VARCHAR2(20); \
clobData CLOB;\
BEGIN \
FOR i IN 1..217 LOOP \
x := x + 1; \
n := 'staff ' || x; \
INSERT INTO nodb_employees VALUES (x, n, EMPTY_CLOB()) RETURNING employees_history INTO clobData; \
\
DBMS_LOB.WRITE(clobData, 20, 1, '12345678901234567890');\
END LOOP; \
END; ";
var connection = null;
var rowsAmount = 217;
before(function (done) {
oracledb.getConnection(credential, function (err, conn) {
if (err) {
console.error(err);
return;
}
connection = conn;
connection.execute(createTable, function (err) {
if (err) {
console.error(err);
return;
}
connection.execute(insertRows, function (err) {
if (err) {
console.error(err);
return;
}
done();
before(function(done) {
async.series([
function getConn(cb) {
oracledb.getConnection(credential, function(err, conn) {
should.not.exist(err);
connection = conn;
cb();
});
});
});
});
},
function createTab(cb) {
var proc = "BEGIN \n" +
" DECLARE \n" +
" e_table_exists EXCEPTION; \n" +
" PRAGMA EXCEPTION_INIT(e_table_exists, -00942);\n " +
" BEGIN \n" +
" EXECUTE IMMEDIATE ('DROP TABLE nodb_employees'); \n" +
" EXCEPTION \n" +
" WHEN e_table_exists \n" +
" THEN NULL; \n" +
" END; \n" +
" EXECUTE IMMEDIATE (' \n" +
" CREATE TABLE nodb_employees ( \n" +
" employees_id NUMBER, \n" +
" employees_name VARCHAR2(20), \n" +
" employees_history CLOB \n" +
" ) \n" +
" '); \n" +
"END; ";
after(function (done) {
connection.execute(
'DROP TABLE nodb_employees',
function (err) {
if (err) {
console.error(err.message);
return;
}
connection.release(function (err) {
if (err) {
console.error(err.message);
return;
connection.execute(
proc,
function(err) {
should.not.exist(err);
cb();
}
done();
});
}
);
});
);
},
function insertRows(cb) {
var proc = "DECLARE \n" +
" x NUMBER := 0; \n" +
" n VARCHAR2(20); \n" +
" clobData CLOB; \n" +
"BEGIN \n" +
" FOR i IN 1..217 LOOP \n" +
" x := x + 1; \n" +
" n := 'staff ' || x; \n" +
" INSERT INTO nodb_employees VALUES (x, n, EMPTY_CLOB()) RETURNING employees_history INTO clobData; \n" +
" DBMS_LOB.WRITE(clobData, 20, 1, '12345678901234567890'); \n" +
" END LOOP; \n" +
"end; ";
describe('13.1 Testing ResultSet stream', function () {
connection.execute(
proc,
function(err) {
should.not.exist(err);
cb();
}
);
}
], done);
}) // before
after(function(done) {
async.series([
function(callback) {
connection.execute(
"DROP TABLE nodb_employees",
function(err) {
should.not.exist(err);
callback();
}
);
},
function(callback) {
connection.release(function(err) {
should.not.exist(err);
callback();
});
},
], done);
}) // after
describe('13.1 Testing QueryStream', function () {
it('13.1.1 stream results for oracle connection', function (done) {
connection.should.be.ok;
@ -357,6 +362,7 @@ describe('13. stream1.js', function () {
var counter = 0;
var clobs = [];
var clobsRead = 0;
stream.on('data', function (data) {
var rowIndex = counter;
@ -487,4 +493,146 @@ describe('13. stream1.js', function () {
});
});
});
describe('13.2 Testing QueryStream._close', function () {
it('13.2.1 should be able to stop the stream early with _close', function (done) {
connection.should.be.ok;
var stream = connection.queryStream('SELECT employees_name FROM nodb_employees');
stream.on('data', function () {
stream.pause();
stream._close();
});
stream.on('close', function() {
done();
});
stream.on('end', function () {
done(new Error('Reached the end of the stream'));
});
stream.on('error', function (err) {
done(err);
});
});
it('13.2.2 should be able to stop the stream before any data', function (done) {
connection.should.be.ok;
var stream = connection.queryStream('SELECT employees_name FROM nodb_employees');
stream.on('close', function() {
done();
});
// Close is synchronous so it needs to be called after the close listener is added.
stream._close();
stream.on('data', function () {
done(new Error('Received data'));
});
stream.on('end', function () {
done(new Error('Reached the end of the stream'));
});
stream.on('error', function (err) {
done(err);
});
});
it('13.2.3 should invoke an optional callback passed to _close', function (done) {
connection.should.be.ok;
var stream = connection.queryStream('SELECT employees_name FROM nodb_employees');
stream._close(function() {
done();
});
stream.on('data', function () {
done(new Error('Received data'));
});
stream.on('end', function () {
done(new Error('Reached the end of the stream'));
});
stream.on('error', function (err) {
done(err);
});
});
});
describe('13.3 Testing QueryStream\'s maxRows control', function () {
it('13.3.1 should use oracledb.maxRows for fetching', function (done) {
var defaultMaxRows;
var testMaxRows = 9;
connection.should.be.ok;
defaultMaxRows = oracledb.maxRows;
oracledb.maxRows = testMaxRows;
var stream = connection.queryStream('SELECT employees_name FROM nodb_employees');
stream.on('data', function () {
stream.pause();
// Using the internal/private caches to validate
should.equal(stream._fetchedRows.length, testMaxRows - (1 + stream._readableState.buffer.length));
stream._close();
});
stream.on('close', function() {
oracledb.maxRows = defaultMaxRows;
done();
});
stream.on('end', function () {
done(new Error('Reached the end of the stream'));
});
stream.on('error', function (err) {
done(err);
});
});
it('13.3.2 should default to 100 if oracledb.maxRows is falsey', function (done) {
var defaultMaxRows;
var testMaxRows = 0;
connection.should.be.ok;
defaultMaxRows = oracledb.maxRows;
oracledb.maxRows = testMaxRows;
var stream = connection.queryStream('SELECT employees_name FROM nodb_employees');
stream.on('data', function () {
stream.pause();
// Using the internal/private caches to validate
should.equal(stream._fetchedRows.length, (99 - stream._readableState.buffer.length));
stream._close();
});
stream.on('close', function() {
oracledb.maxRows = defaultMaxRows;
done();
});
stream.on('end', function () {
done(new Error('Reached the end of the stream'));
});
stream.on('error', function (err) {
done(err);
});
});
});
});

View File

@ -242,20 +242,15 @@ describe('14. stream2.js', function() {
}) // 14.6
it('14.7 Negative - queryStream() has no parameters', function(done) {
var stream;
var stream = connection.queryStream();
stream.on('error', function(error) {
should.exist(error);
// console.log(error);
// NJS-006: invalid type for parameter 1
setTimeout(done, 500);
});
stream.on('data', function(data) {
should.not.exist(data);
});
try {
stream = connection.queryStream();
} catch (err) {
should.exist(err);
err.message.should.eql('NJS-009: invalid number of parameters');
done();
}
})
it('14.8 Negative - give invalid SQL as first parameter', function(done) {