From a8b69f0c0a040291c90ec3cb6d8fbf78c0e8cc50 Mon Sep 17 00:00:00 2001 From: Christopher Jones Date: Mon, 17 Aug 2015 16:19:36 +1000 Subject: [PATCH] Update tests for new functionality --- test/README.md | 19 +- test/autoCommit.js | 420 ++++++++++++---- test/binding.js | 948 +++++++++++++++++++++++++------------ test/clobexample.txt | 5 + test/columnMetadata.js | 2 +- test/connection.js | 302 ++++++++---- test/dataTypeBlob.js | 242 ++++++++++ test/dataTypeClob.js | 241 ++++++++++ test/dataTypeRowid.js | 127 +++++ test/dataTypeTimestamp3.js | 12 +- test/dataTypeTimestamp4.js | 12 +- test/dbConfig.js | 8 +- test/dmlReturning.js | 2 +- test/examples.js | 221 ++++++++- test/fetchAs.js | 260 ++++++++++ test/fuzzydinosaur.jpg | Bin 0 -> 11981 bytes test/list.txt | 339 +++++++++++++ test/nestedCursor.js | 303 ++++++++++++ test/resultSet1.js | 75 ++- test/resultSet2.js | 594 +++++++++++++++++------ 20 files changed, 3462 insertions(+), 670 deletions(-) create mode 100644 test/clobexample.txt create mode 100644 test/dataTypeBlob.js create mode 100644 test/dataTypeClob.js create mode 100644 test/dataTypeRowid.js create mode 100644 test/fetchAs.js create mode 100644 test/fuzzydinosaur.jpg create mode 100644 test/list.txt create mode 100644 test/nestedCursor.js diff --git a/test/README.md b/test/README.md index 7c7ac313..f6cd2c35 100644 --- a/test/README.md +++ b/test/README.md @@ -58,20 +58,21 @@ Note: these are listed in `devDependencies` in `package.json` so `npm install` will install them when executed inside a node-oracledb package directory. -### 4. Edit database credentials +### 4. Database credentials + +The database credentials for node-oracledb test suite are defined in dbConfig.js file. You can set the credentials via environment variables or dbConfig.js file. +Change the credentials to a user who has privileges to connect and create tables. ``` vi /node_modules/oracledb/test/dbConfig.js ``` -Change the credentials to a user who has privileges to connect and create tables: - ```javascript module.exports = { - user : "hr", - password : "welcome", - connectString : "localhost/orcl", - externalAuth : false + user : process.env.NODE_ORACLEDB_USER || "hr", + password : process.env.NODE_ORACLEDB_PASSWORD || "welcome", + connectString : process.env.NODE_ORACLEDB_CONNECTIONSTRING || "localhost/orcl", + externalAuth : process.env.NODE_ORACLEDB_EXTERNALAUTH ? true : false }; ``` @@ -113,3 +114,7 @@ assigned a number. The following number ranges have been chosen: - 1 - 20 are reserved for basic functional tests - 21 - 50 are reserved for data type supporting tests - 51 onwards are for other tests + +## Test List + +See test/list.txt file for the list of existing tests. diff --git a/test/autoCommit.js b/test/autoCommit.js index ee2339c4..2008426c 100644 --- a/test/autoCommit.js +++ b/test/autoCommit.js @@ -31,142 +31,168 @@ * 51 - are for other tests * *****************************************************************************/ +"use strict"; var oracledb = require('oracledb'); var should = require('should'); var async = require('async'); var dbConfig = require('./dbConfig.js'); -describe('7. autoCommit.js', function(){ - +describe('7. autoCommit.js', function() { + if(dbConfig.externalAuth){ var credential = { externalAuth: true, connectString: dbConfig.connectString }; } else { var credential = dbConfig; - } + } - var connection = false; - var anotherConnection = false; - var script = - "BEGIN \ - DECLARE \ - e_table_exists EXCEPTION; \ - PRAGMA EXCEPTION_INIT(e_table_exists, -00942); \ - BEGIN \ - EXECUTE IMMEDIATE ('DROP TABLE oracledb_departments'); \ - EXCEPTION \ - WHEN e_table_exists \ - THEN NULL; \ - END; \ - EXECUTE IMMEDIATE (' \ - CREATE TABLE oracledb_departments ( \ - department_id NUMBER, \ - department_name VARCHAR2(20) \ - ) \ - '); \ - END; "; - - beforeEach(function(done){ - oracledb.outFormat = oracledb.OBJECT; - oracledb.autoCommit = true; - + var pool = null; + var connection = null; + + before('create pool, get one connection, create table', function(done) { + var script = + "BEGIN \ + DECLARE \ + e_table_exists EXCEPTION; \ + PRAGMA EXCEPTION_INIT(e_table_exists, -00942); \ + BEGIN \ + EXECUTE IMMEDIATE ('DROP TABLE oracledb_departments'); \ + EXCEPTION \ + WHEN e_table_exists \ + THEN NULL; \ + END; \ + EXECUTE IMMEDIATE (' \ + CREATE TABLE oracledb_departments ( \ + department_id NUMBER, \ + department_name VARCHAR2(20) \ + ) \ + '); \ + END; "; + async.series([ - function(callback){ - oracledb.getConnection(credential, function(err, conn){ - if(err) { console.error(err.message); return; } - connection = conn; - callback(); - }); - }, - function(callback){ - oracledb.getConnection(credential, function(err, conn){ - if(err) { console.error(err.message); return; } - anotherConnection = conn; - callback(); - }); - }, - function(callback){ - connection.execute(script, function(err){ - if(err) { console.error(err.message); return; } - connection.commit( function(err){ - if(err) { console.error(err.message); return; } - callback(); - }); - }); - } - ], done); - - - }) - - afterEach(function(done){ - oracledb.outFormat = oracledb.ARRAY; - oracledb.autoCommit = false; - - async.series([ - function(callback){ - connection.execute( - 'DROP TABLE oracledb_departments', - function(err){ - if(err) { console.error(err.message); return; } + function(callback) { + oracledb.createPool( + { + externalAuth : credential.externalAuth, + user : credential.user, + password : credential.password, + connectString : credential.connectString, + poolMin : 3, + poolMax : 7, + poolIncrement : 1 + }, + function(err, connectionPool) { + should.not.exist(err); + pool = connectionPool; callback(); } ); }, - function(callback){ - connection.release( function(err){ - if(err) { console.error(err.message); return; } + function(callback) { + pool.getConnection( function(err, conn) { + should.not.exist(err); + connection = conn; + callback(); + }); + }, + function(callback) { + connection.execute( + script, + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + }) + + after('drop table, release connection, terminate pool', function(done) { + async.series([ + function(callback) { + connection.execute( + "DROP TABLE oracledb_departments", + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.release( function(err) { + should.not.exist(err); callback(); }); }, - function(callback){ - anotherConnection.release( function(err){ - if(err) { console.error(err.message); return; } + function(callback) { + pool.terminate(function(err) { + should.not.exist(err); callback(); }); } ], done); }) + + afterEach('truncate table, reset the oracledb properties', function(done) { + oracledb.autoCommit = false; /* Restore to default value */ + + connection.execute( + "TRUNCATE TABLE oracledb_departments", + function(err) { + should.not.exist(err); + done(); + } + ); + }) - it('7.1 auto commit takes effect for DML - insert', function(done){ + it('7.1 autoCommit takes effect when setting oracledb.autoCommit before connecting', function(done) { + var conn1 = null; + var conn2 = null; + + oracledb.autoCommit = true; + async.series([ - function(callback){ - connection.execute( + function(callback) { + pool.getConnection( + function(err, conn) { + should.not.exist(err); + conn1 = conn; + callback(); + } + ); + }, + function(callback) { + conn1.execute( "INSERT INTO oracledb_departments VALUES (82, 'Security')", - function(err){ + function(err) { should.not.exist(err); callback(); } ); }, - function(callback){ - anotherConnection.execute( - "SELECT department_id FROM oracledb_departments WHERE department_name = 'Security'", - function(err, result){ + function(callback) { // get another connection + pool.getConnection( + function(err, conn) { + should.not.exist(err); + conn2 = conn; + callback(); + } + ); + }, + function(callback) { + conn2.execute( + "SELECT department_id FROM oracledb_departments WHERE department_name = 'Security'", + [], + { outFormat: oracledb.OBJECT }, + function(err, result) { should.not.exist(err); - should.exist(result); - // console.log(result); result.rows[0].DEPARTMENT_ID.should.eql(82).and.be.a.Number; callback(); } ); - } - ], done); - }) - - it('7.2 auto commit takes effect for DML - update', function(done){ - async.series([ - function(callback){ - connection.execute( - "INSERT INTO oracledb_departments VALUES (82, 'Security')", - function(err){ - should.not.exist(err); - callback(); - } - ); }, - function(callback){ - connection.execute( + function(callback) { + conn1.execute( "UPDATE oracledb_departments SET department_id = 101 WHERE department_name = 'Security'", function(err){ should.not.exist(err); @@ -174,18 +200,206 @@ describe('7. autoCommit.js', function(){ } ); }, - function(callback){ - anotherConnection.execute( + function(callback) { + conn2.execute( "SELECT department_id FROM oracledb_departments WHERE department_name = 'Security'", - function(err, result){ + [], + { outFormat: oracledb.OBJECT }, + function(err, result) { should.not.exist(err); - should.exist(result); - // console.log(result); result.rows[0].DEPARTMENT_ID.should.eql(101).and.be.a.Number; callback(); } ); + }, + function(callback) { + conn1.release(function(err) { + should.not.exist(err); + callback(); + }); + }, + function(callback) { + conn2.release(function(err) { + should.not.exist(err); + callback(); + }); } ], done); }) + + it('7.2 autoCommit takes effect when setting oracledb.autoCommit after connecting', function(done) { + var conn1 = null; + var conn2 = null; + + async.series([ + function(callback) { + pool.getConnection( + function(err, conn) { + should.not.exist(err); + conn1 = conn; + callback(); + } + ); + }, + function(callback) { + oracledb.autoCommit = true; // change autoCommit after connection + conn1.execute( + "INSERT INTO oracledb_departments VALUES (82, 'Security')", + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + pool.getConnection( + function(err, conn) { + should.not.exist(err); + conn2 = conn; + callback(); + } + ); + }, + function(callback) { + conn2.execute( + "SELECT department_id FROM oracledb_departments WHERE department_name = 'Security'", + [], + { outFormat: oracledb.OBJECT }, + function(err, result) { + should.not.exist(err); + result.rows[0].DEPARTMENT_ID.should.eql(82).and.be.a.Number; + callback(); + } + ); + }, + function(callback) { + conn1.execute( + "UPDATE oracledb_departments SET department_id = 101 WHERE department_name = 'Security'", + function(err){ + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + conn2.execute( + "SELECT department_id FROM oracledb_departments WHERE department_name = 'Security'", + [], + { outFormat: oracledb.OBJECT }, + function(err, result) { + should.not.exist(err); + result.rows[0].DEPARTMENT_ID.should.eql(101).and.be.a.Number; + callback(); + } + ); + }, + function(callback) { + conn1.release(function(err) { + should.not.exist(err); + callback(); + }); + }, + function(callback) { + conn2.release(function(err) { + should.not.exist(err); + callback(); + }); + } + ], done); + }) + + it('7.3 autoCommit setting does not affect previous SQL result', function(done) { + var conn1 = null; + var conn2 = null; + + async.series([ + function(callback) { + pool.getConnection( + function(err, conn) { + should.not.exist(err); + conn1 = conn; + callback(); + } + ); + }, + function(callback) { + conn1.execute( + "INSERT INTO oracledb_departments VALUES (82, 'Security')", + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + pool.getConnection( + function(err, conn) { + should.not.exist(err); + conn2 = conn; + callback(); + } + ); + }, + function(callback) { + oracledb.autoCommit = true; // change autoCommit after connection + conn2.execute( + "SELECT department_id FROM oracledb_departments WHERE department_name = 'Security'", + [], + { outFormat: oracledb.OBJECT }, + function(err, result) { + should.not.exist(err); + (result.rows).should.eql([]); + callback(); + } + ); + }, + function(callback) { + conn2.execute( + "INSERT INTO oracledb_departments VALUES (99, 'Marketing')", + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + conn2.execute( + "SELECT COUNT(*) as amount FROM oracledb_departments", + [], + { outFormat: oracledb.OBJECT }, + function(err, result) { + should.not.exist(err); + result.rows[0].AMOUNT.should.eql(1); + callback(); + } + ); + }, + function(callback) { + conn1.execute( + "SELECT COUNT(*) as amount FROM oracledb_departments", + [], + { outFormat: oracledb.OBJECT }, + function(err, result) { + should.not.exist(err); + result.rows[0].AMOUNT.should.eql(2); // autoCommit for SELECT + callback(); + } + ); + }, + function(callback) { + conn1.release(function(err) { + should.not.exist(err); + callback(); + }); + }, + function(callback) { + conn2.release(function(err) { + should.not.exist(err); + callback(); + }); + } + ], done); + }) + }) + diff --git a/test/binding.js b/test/binding.js index 87ae90f4..49a17e63 100644 --- a/test/binding.js +++ b/test/binding.js @@ -47,318 +47,644 @@ describe('4. binding.js', function() { var credential = dbConfig; } - var connection = false; - beforeEach(function(done) { - oracledb.getConnection(credential, function(err, conn) { - if(err) { console.error(err.message); return; } - connection = conn; - done(); - }); - }) + describe('4.1 test STRING, NUMBER, ARRAY & JSON format', function() { + var connection = null; + before(function(done) { + oracledb.getConnection(credential, function(err, conn) { + if(err) { console.error(err.message); return; } + connection = conn; + done(); + }); + }) - afterEach(function(done) { - connection.release( function(err) { - if(err) { console.error(err.message); return; } - done(); - }); - }) - - - it('4.1 VARCHAR2 binding, Object & Array formats', function(done) { - async.series([ - function(callback) { - var proc = "CREATE OR REPLACE PROCEDURE oracledb_testproc (p_out OUT VARCHAR2) \ - AS \ - BEGIN \ - p_out := 'abcdef'; \ - END;"; - connection.should.be.ok; - connection.execute( - proc, - function(err) { - should.not.exist(err); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "BEGIN oracledb_testproc(:o); END;", - { - o: { type: oracledb.STRING, dir: oracledb.BIND_OUT } - }, - function(err, result) { - should.not.exist(err); - // console.log(result); - result.outBinds.o.should.be.exactly('abcdef'); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "BEGIN oracledb_testproc(:o); END;", - [ - { type: oracledb.STRING, dir: oracledb.BIND_OUT } - ], - function(err, result) { - should.not.exist(err); - // console.log(result); - result.outBinds.should.be.eql(['abcdef']); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "DROP PROCEDURE oracledb_testproc", - function(err) { - should.not.exist(err); - callback(); - } - ); - } - ], done); - }) - - it('4.2 NUMBER binding, Object & Array formats', function(done) { - async.series([ - function(callback) { - var proc = "CREATE OR REPLACE PROCEDURE oracledb_testproc (p_out OUT NUMBER) \ - AS \ - BEGIN \ - p_out := 10010; \ - END;"; - connection.should.be.ok; - connection.execute( - proc, - function(err) { - should.not.exist(err); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "BEGIN oracledb_testproc(:o); END;", - { - o: { type: oracledb.NUMBER, dir: oracledb.BIND_OUT } - }, - function(err, result) { - should.not.exist(err); - // console.log(result); - result.outBinds.o.should.be.exactly(10010); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "BEGIN oracledb_testproc(:o); END;", - [ - { type: oracledb.NUMBER, dir: oracledb.BIND_OUT } - ], - function(err, result) { - should.not.exist(err); - // console.log(result); - result.outBinds.should.be.eql([ 10010 ]); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "DROP PROCEDURE oracledb_testproc", - function(err) { - should.not.exist(err); - callback(); - } - ); - } - ], done); - }) - - it('4.3 Multiple binding values, Object & Array formats', function(done) { - async.series([ - function(callback) { - var proc = "CREATE OR REPLACE PROCEDURE oracledb_testproc (p_in IN VARCHAR2, p_inout IN OUT VARCHAR2, p_out OUT NUMBER) \ + after(function(done) { + connection.release( function(err) { + if(err) { console.error(err.message); return; } + done(); + }); + }) + + it('4.1.1 VARCHAR2 binding, Object & Array formats', function(done) { + async.series([ + function(callback) { + var proc = "CREATE OR REPLACE PROCEDURE oracledb_testproc (p_out OUT VARCHAR2) \ AS \ - BEGIN \ - p_inout := p_in || ' ' || p_inout; \ - p_out := 101; \ - END; "; - connection.should.be.ok; - connection.execute( - proc, - function(err) { - should.not.exist(err); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "BEGIN oracledb_testproc(:i, :io, :o); END;", - { - i: 'Alan', // bind type is determined from the data type - io: { val: 'Turing', dir : oracledb.BIND_INOUT }, - o: { type: oracledb.NUMBER, dir : oracledb.BIND_OUT } - }, - function(err, result) { - should.not.exist(err); - // console.log(result); - result.outBinds.io.should.be.exactly('Alan Turing'); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "BEGIN oracledb_testproc(:i, :io, :o); END;", - [ - 'Alan', // bind type is determined from the data type - { val: 'Turing', dir : oracledb.BIND_INOUT }, - { type: oracledb.NUMBER, dir : oracledb.BIND_OUT } - ], - function(err, result) { - should.not.exist(err); - // console.log(result); - result.outBinds.should.be.eql([ 'Alan Turing', 101 ]); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "DROP PROCEDURE oracledb_testproc", - function(err) { - should.not.exist(err); - callback(); - } - ); - } - ], done); - }) - - it('4.4 Multiple binding values, Change binding order', function(done) { - async.series([ - function(callback) { - var proc = "CREATE OR REPLACE PROCEDURE oracledb_testproc (p_inout IN OUT VARCHAR2, p_out OUT NUMBER, p_in IN VARCHAR2) \ - AS \ - BEGIN \ - p_inout := p_in || ' ' || p_inout; \ - p_out := 101; \ - END; "; - connection.should.be.ok; - connection.execute( - proc, - function(err) { - should.not.exist(err); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "BEGIN oracledb_testproc(:io, :o, :i); END;", - { - i: 'Alan', // bind type is determined from the data type - io: { val: 'Turing', dir : oracledb.BIND_INOUT }, - o: { type: oracledb.NUMBER, dir : oracledb.BIND_OUT } - }, - function(err, result) { - should.not.exist(err); - // console.log(result); - result.outBinds.io.should.be.exactly('Alan Turing'); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "BEGIN oracledb_testproc(:io, :o, :i); END;", - [ - { val: 'Turing', dir : oracledb.BIND_INOUT }, - { type: oracledb.NUMBER, dir : oracledb.BIND_OUT }, - 'Alan', // bind type is determined from the data type - ], - function(err, result) { - should.not.exist(err); - // console.log(result); - result.outBinds.should.be.eql([ 'Alan Turing', 101 ]); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "DROP PROCEDURE oracledb_testproc", - function(err) { - should.not.exist(err); - callback(); - } - ); - } - ], done); - }) - - it('4.5 outBind & maxSize restriction', function(done) { - async.series([ - function(callback) { - var proc = "CREATE OR REPLACE PROCEDURE oracledb_testproc (p_out OUT VARCHAR2) \ - AS \ - BEGIN \ - p_out := 'ABCDEF GHIJK LMNOP QRSTU'; \ - END;"; - connection.should.be.ok; - connection.execute( - proc, - function(err) { - should.not.exist(err); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "BEGIN oracledb_testproc(:o); END;", - { - o: { type: oracledb.STRING, dir: oracledb.BIND_OUT, maxSize:2 } - }, - function(err, result) { - should.exist(err); - // console.log(err.message); - err.message.should.startWith('ORA-06502:'); - // console.log(result); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "BEGIN oracledb_testproc(:o); END;", - [ - { type: oracledb.STRING, dir: oracledb.BIND_OUT, maxSize:3 } - ], - function(err, result) { - should.exist(err); - // console.log(err.message); - err.message.should.startWith('ORA-06502:'); - // console.log(result); - callback(); - } - ); - }, - function(callback) { - connection.execute( - "DROP PROCEDURE oracledb_testproc", - function(err) { - should.not.exist(err); - callback(); - } - ); - } - ], done); - }) + BEGIN \ + p_out := 'abcdef'; \ + END;"; + connection.should.be.ok; + connection.execute( + proc, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "BEGIN oracledb_testproc(:o); END;", + { + o: { type: oracledb.STRING, dir: oracledb.BIND_OUT } + }, + function(err, result) { + should.not.exist(err); + // console.log(result); + result.outBinds.o.should.be.exactly('abcdef'); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "BEGIN oracledb_testproc(:o); END;", + [ + { type: oracledb.STRING, dir: oracledb.BIND_OUT } + ], + function(err, result) { + should.not.exist(err); + // console.log(result); + result.outBinds.should.be.eql(['abcdef']); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "DROP PROCEDURE oracledb_testproc", + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + }) + it('4.1.2 NUMBER binding, Object & Array formats', function(done) { + async.series([ + function(callback) { + var proc = "CREATE OR REPLACE PROCEDURE oracledb_testproc (p_out OUT NUMBER) \ + AS \ + BEGIN \ + p_out := 10010; \ + END;"; + connection.should.be.ok; + connection.execute( + proc, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "BEGIN oracledb_testproc(:o); END;", + { + o: { type: oracledb.NUMBER, dir: oracledb.BIND_OUT } + }, + function(err, result) { + should.not.exist(err); + // console.log(result); + result.outBinds.o.should.be.exactly(10010); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "BEGIN oracledb_testproc(:o); END;", + [ + { type: oracledb.NUMBER, dir: oracledb.BIND_OUT } + ], + function(err, result) { + should.not.exist(err); + // console.log(result); + result.outBinds.should.be.eql([ 10010 ]); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "DROP PROCEDURE oracledb_testproc", + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + }) + + it('4.1.3 Multiple binding values, Object & Array formats', function(done) { + async.series([ + function(callback) { + var proc = "CREATE OR REPLACE PROCEDURE oracledb_testproc (p_in IN VARCHAR2, p_inout IN OUT VARCHAR2, p_out OUT NUMBER) \ + AS \ + BEGIN \ + p_inout := p_in || ' ' || p_inout; \ + p_out := 101; \ + END; "; + connection.should.be.ok; + connection.execute( + proc, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "BEGIN oracledb_testproc(:i, :io, :o); END;", + { + i: 'Alan', // bind type is determined from the data type + io: { val: 'Turing', dir : oracledb.BIND_INOUT }, + o: { type: oracledb.NUMBER, dir : oracledb.BIND_OUT } + }, + function(err, result) { + should.not.exist(err); + // console.log(result); + result.outBinds.io.should.be.exactly('Alan Turing'); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "BEGIN oracledb_testproc(:i, :io, :o); END;", + [ + 'Alan', // bind type is determined from the data type + { val: 'Turing', dir : oracledb.BIND_INOUT }, + { type: oracledb.NUMBER, dir : oracledb.BIND_OUT } + ], + function(err, result) { + should.not.exist(err); + // console.log(result); + result.outBinds.should.be.eql([ 'Alan Turing', 101 ]); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "DROP PROCEDURE oracledb_testproc", + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + }) + + it('4.1.4 Multiple binding values, Change binding order', function(done) { + async.series([ + function(callback) { + var proc = "CREATE OR REPLACE PROCEDURE oracledb_testproc (p_inout IN OUT VARCHAR2, p_out OUT NUMBER, p_in IN VARCHAR2) \ + AS \ + BEGIN \ + p_inout := p_in || ' ' || p_inout; \ + p_out := 101; \ + END; "; + connection.should.be.ok; + connection.execute( + proc, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "BEGIN oracledb_testproc(:io, :o, :i); END;", + { + i: 'Alan', // bind type is determined from the data type + io: { val: 'Turing', dir : oracledb.BIND_INOUT }, + o: { type: oracledb.NUMBER, dir : oracledb.BIND_OUT } + }, + function(err, result) { + should.not.exist(err); + // console.log(result); + result.outBinds.io.should.be.exactly('Alan Turing'); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "BEGIN oracledb_testproc(:io, :o, :i); END;", + [ + { val: 'Turing', dir : oracledb.BIND_INOUT }, + { type: oracledb.NUMBER, dir : oracledb.BIND_OUT }, + 'Alan', // bind type is determined from the data type + ], + function(err, result) { + should.not.exist(err); + // console.log(result); + result.outBinds.should.be.eql([ 'Alan Turing', 101 ]); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "DROP PROCEDURE oracledb_testproc", + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + }) + + it('4.1.5 default bind type - STRING', function(done) { + connection.should.be.ok; + var sql = "begin :n := 1001; end;"; + var bindVar = { n : { dir: oracledb.BIND_OUT } }; + var options = { }; + + connection.execute( + sql, + bindVar, + options, + function(err, result) { + should.not.exist(err); + // console.log(result); + result.outBinds.n.should.be.a.String; + result.outBinds.n.should.eql('1001'); + done(); + } + ); + }) + + }) + + describe('4.2 mixing named with positional binding', function() { + var connection = null; + var createTable = + "BEGIN \ + DECLARE \ + e_table_exists EXCEPTION; \ + PRAGMA EXCEPTION_INIT(e_table_exists, -00942); \ + BEGIN \ + EXECUTE IMMEDIATE ('DROP TABLE oracledb_binding'); \ + EXCEPTION \ + WHEN e_table_exists \ + THEN NULL; \ + END; \ + EXECUTE IMMEDIATE (' \ + CREATE TABLE oracledb_binding ( \ + id NUMBER(4), \ + name VARCHAR2(32) \ + ) \ + '); \ + END; "; + var insert = 'insert into oracledb_binding (id, name) values (:0, :1) returning id into :2'; + var param1 = [ 1, 'changjie', { type: oracledb.NUMBER, dir: oracledb.BIND_OUT } ]; + var param2 = [ 2, 'changjie', { ignored_name: { type: oracledb.NUMBER, dir: oracledb.BIND_OUT } } ]; + var options = { autoCommit: true, outFormat: oracledb.OBJECT }; + + beforeEach(function(done) { + oracledb.getConnection(credential, function(err, conn) { + should.not.exist(err); + connection = conn; + conn.execute( + createTable, + function(err) { + should.not.exist(err); + done(); + } + ); + }); + }) + + afterEach(function(done) { + connection.should.be.ok; + connection.execute( + "DROP TABLE oracledb_binding", + function(err) { + should.not.exist(err); + connection.release(function(err) { + should.not.exist(err); + done(); + }); + } + ); + }) + + it('4.2.1 array binding is ok', function(done) { + connection.execute( + insert, + param1, + options, + function(err, result) { + should.not.exist(err); + result.rowsAffected.should.be.exactly(1); + result.outBinds[0].should.eql([1]); + // console.log(result); + connection.execute( + "SELECT * FROM oracledb_binding", + [], + options, + function(err, result) { + should.not.exist(err); + //console.log(result); + result.rows[0].ID.should.be.exactly(1); + result.rows[0].NAME.should.eql('changjie'); + done(); + } + ); + } + ); + }) + + it.skip('4.2.2 array binding with mixing JSON should throw an error', function(done) { + connection.execute( + insert, + param2, + options, + function(err, result) { + should.exist(err); // pending to fix + result.rowsAffected.should.be.exactly(1); + //result.outBinds[0].should.eql([1]); + //console.log(result); + connection.execute( + "SELECT * FROM oracledb_binding", + [], + options, + function(err, result) { + should.not.exist(err); + //console.log(result); + result.rows[0].ID.should.be.exactly(2); + result.rows[0].NAME.should.eql('changjie'); + done(); + } + ); + } + ); + }) + + }) + + describe('4.3 insert with DATE column and DML returning', function(done) { + var connection = null; + var createTable = + "BEGIN \ + DECLARE \ + e_table_exists EXCEPTION; \ + PRAGMA EXCEPTION_INIT(e_table_exists, -00942); \ + BEGIN \ + EXECUTE IMMEDIATE ('DROP TABLE oracledb_binding'); \ + EXCEPTION \ + WHEN e_table_exists \ + THEN NULL; \ + END; \ + EXECUTE IMMEDIATE (' \ + CREATE TABLE oracledb_binding ( \ + num NUMBER(4), \ + str VARCHAR2(32), \ + dt DATE \ + ) \ + '); \ + END; "; + + beforeEach(function(done) { + oracledb.getConnection(credential, function(err, conn) { + should.not.exist(err); + connection = conn; + conn.execute( + createTable, + function(err) { + should.not.exist(err); + done(); + } + ); + }); + }) + + afterEach(function(done) { + connection.should.be.ok; + connection.execute( + "DROP TABLE oracledb_binding", + function(err) { + should.not.exist(err); + connection.release(function(err) { + should.not.exist(err); + done(); + }); + } + ); + }) + + var insert1 = 'insert into oracledb_binding (num, str, dt) values (:0, :1, :2)'; + var insert2 = 'insert into oracledb_binding (num, str, dt) values (:0, :1, :2) returning num into :3'; + var param1 = { 0: 123, 1: 'str', 2: new Date() }; + var param2 = { 0: 123, 1: 'str', 2: new Date(), 3: { type: oracledb.NUMBER, dir: oracledb.BIND_OUT } }; + var param3 = [ 123, 'str', new Date() ]; + var param4 = [ 123, 'str', new Date(), { type: oracledb.NUMBER, dir: oracledb.BIND_OUT } ]; + + var options = { autoCommit: true }; + + it('4.3.1 passes in object syntax without returning into', function(done) { + connection.execute( + insert1, + param1, + options, + function(err, result) { + should.not.exist(err); + result.rowsAffected.should.be.exactly(1); + connection.execute( + "SELECT * FROM oracledb_binding", + [], + options, + function(err, result) { + should.not.exist(err); + // console.log(result); + done(); + } + ); + } + ); + }) + + it('4.3.2 passes in object syntax with returning into', function(done) { + connection.execute( + insert2, + param2, + options, + function(err, result) { + should.not.exist(err); + result.rowsAffected.should.be.exactly(1); + //console.log(result); + result.outBinds.should.eql({ '3': [123] }); + connection.execute( + "SELECT * FROM oracledb_binding", + [], + options, + function(err, result) { + should.not.exist(err); + // console.log(result); + done(); + } + ); + } + ); + }) + + it('4.3.3 passes in array syntax without returning into', function(done) { + connection.execute( + insert1, + param3, + options, + function(err, result) { + should.not.exist(err); + result.rowsAffected.should.be.exactly(1); + // console.log(result); + connection.execute( + "SELECT * FROM oracledb_binding", + [], + options, + function(err, result) { + should.not.exist(err); + // console.log(result); + done(); + } + ); + } + ); + }) + + it ('4.3.4 should pass but fail in array syntax with returning into', function(done) { + connection.execute( + insert2, + param4, + options, + function(err, result) { + should.not.exist(err); + result.rowsAffected.should.be.exactly(1); + // console.log(result); + result.outBinds[0].should.eql([123]); + connection.execute( + "SELECT * FROM oracledb_binding", + [], + options, + function(err, result) { + should.not.exist(err); + // console.log(result); + done(); + } + ); + } + ); + }) + + }) + + describe('4.4 test maxSize option', function() { + var connection = null; + + before(function(done) { + oracledb.getConnection(credential, function(err, conn) { + if(err) { console.error(err.message); return; } + connection = conn; + done(); + }); + }) + + after(function(done) { + connection.release( function(err) { + if(err) { console.error(err.message); return; } + done(); + }); + }) + + it('4.4.1 outBind & maxSize restriction', function(done) { + async.series([ + function(callback) { + var proc = "CREATE OR REPLACE PROCEDURE oracledb_testproc (p_out OUT VARCHAR2) \ + AS \ + BEGIN \ + p_out := 'ABCDEF GHIJK LMNOP QRSTU'; \ + END;"; + connection.should.be.ok; + connection.execute( + proc, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "BEGIN oracledb_testproc(:o); END;", + { + o: { type: oracledb.STRING, dir: oracledb.BIND_OUT, maxSize:2 } + }, + function(err, result) { + should.exist(err); + // console.log(err.message); + err.message.should.startWith('ORA-06502:'); // ORA-06502: PL/SQL: numeric or value error: character string buffer too small + // console.log(result); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "BEGIN oracledb_testproc(:o); END;", + [ + { type: oracledb.STRING, dir: oracledb.BIND_OUT, maxSize:22 } + ], + function(err, result) { + should.exist(err); + // console.log(err.message); + err.message.should.startWith('ORA-06502:'); + // console.log(result); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "DROP PROCEDURE oracledb_testproc", + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + }) + + it.skip('4.4.2 default value is 200', function(done) { + connection.execute( + "BEGIN :o := lpad('A',201,'x'); END;", + { o: { type: oracledb.STRING, dir : oracledb.BIND_OUT } }, + function (err, result) { + should.exist(err); + err.message.should.startWith('ORA-06502:'); + console.log(result.outBinds.o.length); + done(); + } + ); + }) + + it.skip('4.4.3 maximum value is 32767', function(done) { + connection.execute( + "BEGIN :o := lpad('A',32767,'x'); END;", + { o: { type: oracledb.STRING, dir : oracledb.BIND_OUT, maxSize:50000 } }, + function(err, result) { + should.exist(err); + console.log(result.outBinds.o.length); + done(); + } + ); + }) + }) }) diff --git a/test/clobexample.txt b/test/clobexample.txt new file mode 100644 index 00000000..55074fdc --- /dev/null +++ b/test/clobexample.txt @@ -0,0 +1,5 @@ +This is example text used for node-oracledb CLOB examples. + +The Oracle Database Node.js driver powers high performance Node.js applications. + +The node-oracledb home page is at http://www.oracle.com/technetwork/database/database-technologies/node_js/index.html diff --git a/test/columnMetadata.js b/test/columnMetadata.js index 9edb3e26..b0376592 100644 --- a/test/columnMetadata.js +++ b/test/columnMetadata.js @@ -258,7 +258,7 @@ describe('9. columnMetadata.js', function(){ ], done); }) - it('9.8 only works for SELECT statament, does not work for INSERT', function(done){ + it('9.8 only works for SELECT statement, does not work for INSERT', function(done){ connection.should.be.ok; connection.execute( "INSERT INTO oracledb_departments VALUES (99, 'FACILITY', 456, 1700)", diff --git a/test/connection.js b/test/connection.js index 3a2b3717..e1252f97 100644 --- a/test/connection.js +++ b/test/connection.js @@ -141,7 +141,7 @@ describe('1. connection.js', function(){ ); }) - it('1.1.4 Negatve test - invalid outFormat value', function(done){ + it('1.1.4 Negative test - invalid outFormat value', function(done){ connection.should.be.ok; connection.execute( query, {id: 20}, {outFormat:0 }, @@ -186,6 +186,7 @@ describe('1. connection.js', function(){ INSERT INTO oracledb_employees VALUES (x, n); \ END LOOP; \ END; "; + var rowsAmount = 107; before(function(done){ oracledb.getConnection(credential, function(err, conn) { @@ -274,6 +275,19 @@ describe('1. connection.js', function(){ } ); }) + + it('1.2.5 sets maxRows to be very large value', function(done) { + connection.execute( + "SELECT * FROM oracledb_employees", + {}, + {maxRows: 500000}, + function(err, result){ + should.not.exist(err); + (result.rows.length).should.eql(rowsAmount); + done(); + } + ); + }) }) describe('1.3 can call PL/SQL procedures', function(){ @@ -309,7 +323,7 @@ describe('1. connection.js', function(){ ); }) - it('bind parameters in various ways', function(done){ + it('1.3.1 bind parameters in various ways', function(done){ var bindValues = { i: 'Alan', // default is type STRING and direction Infinity io: { val: 'Turing', type: oracledb.STRING, dir: oracledb.BIND_INOUT }, @@ -329,9 +343,7 @@ describe('1. connection.js', function(){ }) }) - describe('1.4 stmtCacheSize = 0, which disable statement caching', function() { - var connection = false; - + describe('1.4 statementCacheSize controls statement caching', function() { var makeTable = "BEGIN \ DECLARE \ @@ -364,12 +376,12 @@ describe('1. connection.js', function(){ VALUES \ (2001, ''Karen Morton'') \ '); \ - END; "; + END; "; + var connection = false; var defaultStmtCache = oracledb.stmtCacheSize; // 30 - - before('get connection and prepare table', function(done) { - oracledb.stmtCacheSize = 0; + + beforeEach('get connection and prepare table', function(done) { oracledb.getConnection(credential, function(err, conn) { if(err) { console.error(err.message); return; } connection = conn; @@ -383,7 +395,7 @@ describe('1. connection.js', function(){ }); }) - after('drop table and release connection', function(done) { + afterEach('drop table and release connection', function(done) { oracledb.stmtCacheSize = defaultStmtCache; connection.execute( "DROP TABLE oracledb_employees", @@ -396,10 +408,51 @@ describe('1. connection.js', function(){ } ); }) - - it('works well when statement cache disabled', function(done) { + + it('1.4.1 stmtCacheSize = 0, which disable statement caching', function(done) { connection.should.be.ok; - (oracledb.stmtCacheSize).should.be.exactly(0); + oracledb.stmtCacheSize = 0; + + async.series([ + function(callback) { + connection.execute( + "INSERT INTO oracledb_employees VALUES (:num, :str)", + { num: 1003, str: 'Robyn Sands' }, + { autoCommit: true }, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "INSERT INTO oracledb_employees VALUES (:num, :str)", + { num: 1004, str: 'Bryant Lin' }, + { autoCommit: true }, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "INSERT INTO oracledb_employees VALUES (:num, :str)", + { num: 1005, str: 'Patrick Engebresson' }, + { autoCommit: true }, + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + }) + + it('1.4.2 works well when statement cache enabled (stmtCacheSize > 0) ', function(done) { + connection.should.be.ok; + oracledb.stmtCacheSize = 100; async.series([ function(callback) { @@ -437,11 +490,10 @@ describe('1. connection.js', function(){ } ], done); }) + }) - describe('1.5 stmtCacheSize > 0', function() { - var connection = false; - + describe('1.5 Testing commit() & rollback() functions', function() { var makeTable = "BEGIN \ DECLARE \ @@ -462,83 +514,39 @@ describe('1. connection.js', function(){ EXECUTE IMMEDIATE (' \ INSERT INTO oracledb_employees \ VALUES \ - (1001,''Chris Jones'') \ + (1001,''Tom Kyte'') \ '); \ EXECUTE IMMEDIATE (' \ INSERT INTO oracledb_employees \ VALUES \ - (1002,''Tom Kyte'') \ + (1002, ''Karen Morton'') \ '); \ - EXECUTE IMMEDIATE (' \ - INSERT INTO oracledb_employees \ - VALUES \ - (2001, ''Karen Morton'') \ - '); \ - END; "; + END; "; - var defaultStmtCache = oracledb.stmtCacheSize; // 30 - - before('get connection and prepare table', function(done) { - oracledb.stmtCacheSize = 100; - oracledb.getConnection(credential, function(err, conn) { - if(err) { console.error(err.message); return; } - connection = conn; - conn.execute( - makeTable, - function(err){ - if(err) { console.error(err.message); return; } - done(); - } - ); - }); - }) - - after('drop table and release connection', function(done) { - oracledb.stmtCacheSize = defaultStmtCache; - connection.execute( - "DROP TABLE oracledb_employees", - function(err){ - if(err) { console.error(err.message); return; } - connection.release( function(err){ - if(err) { console.error(err.message); return; } - done(); - }); - } - ); - }) - - it('works well when statement cache enabled', function(done) { - connection.should.be.ok; - (oracledb.stmtCacheSize).should.be.exactly(100); - + var conn1 = false; + var conn2 = false; + beforeEach('get 2 connections and create the table', function(done) { async.series([ function(callback) { - connection.execute( - "INSERT INTO oracledb_employees VALUES (:num, :str)", - { num: 1003, str: 'Robyn Sands' }, - { autoCommit: true }, - function(err) { - should.not.exist(err); - callback(); - } - ); + oracledb.getConnection(credential, function(err, conn) { + should.not.exist(err); + conn1 = conn; + callback(); + }); }, function(callback) { - connection.execute( - "INSERT INTO oracledb_employees VALUES (:num, :str)", - { num: 1004, str: 'Bryant Lin' }, - { autoCommit: true }, - function(err) { - should.not.exist(err); - callback(); - } - ); + oracledb.getConnection(credential, function(err, conn) { + should.not.exist(err); + conn2 = conn; + callback(); + }); }, function(callback) { - connection.execute( - "INSERT INTO oracledb_employees VALUES (:num, :str)", - { num: 1005, str: 'Patrick Engebresson' }, - { autoCommit: true }, + conn1.should.be.ok; + conn1.execute( + makeTable, + [], + { autoCommit: true }, function(err) { should.not.exist(err); callback(); @@ -547,6 +555,138 @@ describe('1. connection.js', function(){ } ], done); }) + + afterEach('drop table and release connections', function(done) { + conn1.should.be.ok; + conn2.should.be.ok; + async.series([ + function(callback) { + conn2.execute( + "DROP TABLE oracledb_employees", + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + conn1.release(function(err) { + should.not.exist(err); + callback(); + }); + }, + function(callback) { + conn2.release(function(err) { + should.not.exist(err); + callback(); + }); + } + ], done); + }) + + + it('1.5.1 commit() function works well', function(done) { + async.series([ + function(callback) { + conn2.execute( + "INSERT INTO oracledb_employees VALUES (:num, :str)", + { num: 1003, str: 'Patrick Engebresson' }, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + conn1.execute( + "SELECT COUNT(*) FROM oracledb_employees", + function(err, result) { + should.not.exist(err); + result.rows[0][0].should.be.exactly(2); + callback(); + } + ); + }, + function(callback) { + conn2.execute( + "SELECT COUNT(*) FROM oracledb_employees", + function(err, result) { + should.not.exist(err); + result.rows[0][0].should.be.exactly(3); + callback(); + } + ); + }, + function(callback) { + conn2.commit(function(err) { + should.not.exist(err); + callback(); + }); + }, + function(callback) { + conn1.execute( + "SELECT COUNT(*) FROM oracledb_employees", + function(err, result) { + should.not.exist(err); + result.rows[0][0].should.be.exactly(3); + callback(); + } + ); + }, + ], done); + + }) + + it('1.5.2 rollback() function works well', function(done) { + async.series([ + function(callback) { + conn2.execute( + "INSERT INTO oracledb_employees VALUES (:num, :str)", + { num: 1003, str: 'Patrick Engebresson' }, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + conn1.execute( + "SELECT COUNT(*) FROM oracledb_employees", + function(err, result) { + should.not.exist(err); + result.rows[0][0].should.be.exactly(2); + callback(); + } + ); + }, + function(callback) { + conn2.execute( + "SELECT COUNT(*) FROM oracledb_employees", + function(err, result) { + should.not.exist(err); + result.rows[0][0].should.be.exactly(3); + callback(); + } + ); + }, + function(callback) { + conn2.rollback(function(err) { + should.not.exist(err); + callback(); + }); + }, + function(callback) { + conn2.execute( + "SELECT COUNT(*) FROM oracledb_employees", + function(err, result) { + should.not.exist(err); + result.rows[0][0].should.be.exactly(2); + callback(); + } + ); + }, + ], done); + }) }) }) diff --git a/test/dataTypeBlob.js b/test/dataTypeBlob.js new file mode 100644 index 00000000..e31d45a2 --- /dev/null +++ b/test/dataTypeBlob.js @@ -0,0 +1,242 @@ +/* Copyright (c) 2015, 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 + * 41. dataTypeBlob.js + * + * DESCRIPTION + * Testing Oracle data type support - BLOB. + * This test corresponds to example files: + * blobinsert1.js, blobstream1.js and blobstream2.js + * Firstly, Loads an image data and INSERTs it into a BLOB column. + * Secondly, SELECTs the BLOB and pipes it to a file, blobstreamout.jpg + * Thirdly, SELECTs the BLOB and compares it with the original image + * + * 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 + * + *****************************************************************************/ + +var oracledb = require('oracledb'); +var fs = require('fs'); +var async = require('async'); +var should = require('should'); +var dbConfig = require('./dbConfig.js'); +var assist = require('./dataTypeAssist.js'); + +var inFileName = './test/fuzzydinosaur.jpg'; // contains the image to be inserted +var outFileName = './test/blobstreamout.jpg'; + +describe('41. dataTypeBlob', function() { + if(dbConfig.externalAuth){ + var credential = { externalAuth: true, connectString: dbConfig.connectString }; + } else { + var credential = dbConfig; + } + + var connection = null; + var tableName = "oracledb_myblobs"; + var sqlCreate = + "BEGIN " + + " DECLARE " + + " e_table_exists EXCEPTION; " + + " PRAGMA EXCEPTION_INIT(e_table_exists, -00942); " + + " BEGIN " + + " EXECUTE IMMEDIATE ('DROP TABLE " + tableName + " '); " + + " EXCEPTION " + + " WHEN e_table_exists " + + " THEN NULL; " + + " END; " + + " EXECUTE IMMEDIATE (' " + + " CREATE TABLE " + tableName +" ( " + + " num NUMBER, " + + " content BLOB " + + " )" + + " '); " + + "END; "; + + before(function(done) { + oracledb.getConnection(credential, function(err, conn) { + if(err) { console.error(err.message); return; } + connection = conn; + conn.execute( + sqlCreate, + function(err) { + if(err) { console.error(err.message); return; } + done(); + } + ); + }); + }) + + after( function(done){ + connection.execute( + "DROP table " + tableName, + function(err) { + if(err) { console.error(err.message); return; } + connection.release( function(err) { + if(err) { console.error(err.message); return; } + done(); + }); + } + ); + }) + + it('41.1 processes null value correctly', function(done) { + assist.nullValueSupport(connection, tableName, done); + }) + + it('41.2 stores BLOB value correctly', function(done) { + connection.should.be.ok; + async.series([ + function blobinsert1(callback) { + var streamEndEventFired = false; + setTimeout( function() { + streamEndEventFired.should.equal(true, "inStream does not call 'end' event!") + callback(); + }, 500); + + connection.execute( + "INSERT INTO oracledb_myblobs (num, content) VALUES (:n, EMPTY_BLOB()) RETURNING content INTO :lobbv", + { n: 2, lobbv: {type: oracledb.BLOB, dir: oracledb.BIND_OUT} }, + { autoCommit: false }, // a transaction needs to span the INSERT and pipe() + function(err, result) { + should.not.exist(err); + (result.rowsAffected).should.be.exactly(1); + (result.outBinds.lobbv.length).should.be.exactly(1); + + var inStream = fs.createReadStream(inFileName); + inStream.on('error', function(err) { + should.not.exist(err, "inStream.on 'end' event"); + }); + + inStream.on('end', function() { + streamEndEventFired = true; + // now commit updates + connection.commit( function(err) { + should.not.exist(err); + }); + }); + + var lob = result.outBinds.lobbv[0]; + + lob.on('error', function(err) { + should.not.exist(err, "lob.on 'error' event"); + }); + + inStream.pipe(lob); // pipes the data to the BLOB + } + ); + }, + function blobstream1(callback) { + var streamFinishEventFired = false; + setTimeout( function() { + streamFinishEventFired.should.equal(true, "stream does not call 'finish' Event!"); + callback(); + }, 500); + + connection.execute( + "SELECT content FROM oracledb_myblobs WHERE num = :n", + { n: 2 }, + function(err, result) { + should.not.exist(err); + + var lob = result.rows[0][0]; + should.exist(lob); + + lob.on('error', function(err) { + should.not.exist(err, "lob.on 'end' event"); + }); + + var outStream = fs.createWriteStream(outFileName); + + outStream.on('error', function(err) { + should.not.exist(err, "outStream.on 'end' event"); + }); + + lob.pipe(outStream); + outStream.on('finish', function() { + fs.readFile( inFileName, function(err, originalData) { + should.not.exist(err); + + fs.readFile( outFileName, function(err, generatedData) { + should.not.exist(err); + originalData.should.eql(generatedData); + + streamFinishEventFired = true; + }); + }); + + }); // finish event + } + ); + }, + function blobstream2(callback) { + var lobEndEventFired = false; + var lobDataEventFired = false; + setTimeout( function(){ + lobDataEventFired.should.equal(true, "lob does not call 'data' event!"); + lobEndEventFired.should.equal(true, "lob does not call 'end' event!"); + callback(); + }, 500); + + connection.execute( + "SELECT content FROM oracledb_myblobs WHERE num = :n", + { n: 2 }, + function(err, result) { + should.not.exist(err); + + var blob = Buffer(0); + var blobLength = 0; + var lob = result.rows[0][0]; + + should.exist(lob); + + lob.on('error', function(err) { + should.not.exist(err, "lob.on 'end' event"); + }); + + lob.on('data', function(chunk) { + // console.log("lob.on 'data' event"); + // console.log(' - got %d bytes of data', chunk.length); + lobDataEventFired = true; + blobLength = blobLength + chunk.length; + blob = Buffer.concat([blob, chunk], blobLength); + }); + + lob.on('end', function() { + fs.readFile( inFileName, function(err, data) { + should.not.exist(err); + lobEndEventFired = true; + + data.length.should.be.exactly(blob.length); + data.should.eql(blob); + }); + }); // end event + + } + ); + } + ], done); + }) +}) diff --git a/test/dataTypeClob.js b/test/dataTypeClob.js new file mode 100644 index 00000000..be91c5fa --- /dev/null +++ b/test/dataTypeClob.js @@ -0,0 +1,241 @@ +/* Copyright (c) 2015, 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 + * 40. dataTypeClob.js + * + * DESCRIPTION + * Testing Oracle data type support - CLOB. + * This test corresponds to example files: + * clobinsert1.js, clobstream1.js and clobstream2.js + * Firstly, reads text from clobexample.txt and INSERTs it into a CLOB column. + * Secondly, SELECTs a CLOB and pipes it to a file, clobstreamout.txt + * Thirdly, SELECTs the CLOB and compares it with the content in clobexample.txt + * + * 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 + * + *****************************************************************************/ + +var oracledb = require('oracledb'); +var fs = require('fs'); +var async = require('async'); +var should = require('should'); +var dbConfig = require('./dbConfig.js'); +var assist = require('./dataTypeAssist.js'); + +var inFileName = './test/clobexample.txt'; // the file with text to be inserted into the database +var outFileName = './test/clobstreamout.txt'; + +describe('40. dataTypeClob.js', function() { + + if(dbConfig.externalAuth){ + var credential = { externalAuth: true, connectString: dbConfig.connectString }; + } else { + var credential = dbConfig; + } + + var connection = null; + var tableName = "oracledb_myclobs"; + var sqlCreate = + "BEGIN " + + " DECLARE " + + " e_table_exists EXCEPTION; " + + " PRAGMA EXCEPTION_INIT(e_table_exists, -00942); " + + " BEGIN " + + " EXECUTE IMMEDIATE ('DROP TABLE " + tableName + " '); " + + " EXCEPTION " + + " WHEN e_table_exists " + + " THEN NULL; " + + " END; " + + " EXECUTE IMMEDIATE (' " + + " CREATE TABLE " + tableName +" ( " + + " num NUMBER, " + + " content CLOB " + + " )" + + " '); " + + "END; "; + + before(function(done) { + oracledb.getConnection(credential, function(err, conn) { + if(err) { console.error(err.message); return; } + connection = conn; + conn.execute( + sqlCreate, + function(err) { + if(err) { console.error(err.message); return; } + done(); + } + ); + }); + }) + + after( function(done){ + connection.execute( + "DROP table " + tableName, + function(err) { + if(err) { console.error(err.message); return; } + connection.release( function(err) { + if(err) { console.error(err.message); return; } + done(); + }); + } + ); + }) + + it('40.1 processes null value correctly', function(done) { + assist.nullValueSupport(connection, tableName, done); + }) + + it('40.2 stores CLOB value correctly', function(done) { + connection.should.be.ok; + async.series([ + function clobinsert1(callback) { + var streamEndEventFired = false; + setTimeout( function() { + streamEndEventFired.should.equal(true, "inStream does not call 'end' event!") + callback(); + }, 500); + + connection.execute( + "INSERT INTO oracledb_myclobs (num, content) VALUES (:n, EMPTY_CLOB()) RETURNING content INTO :lobbv", + { n: 1, lobbv: {type: oracledb.CLOB, dir: oracledb.BIND_OUT} }, + { autoCommit: false }, // a transaction needs to span the INSERT and pipe() + function(err, result) { + should.not.exist(err); + (result.rowsAffected).should.be.exactly(1); + (result.outBinds.lobbv.length).should.be.exactly(1); + + var inStream = fs.createReadStream(inFileName); + var lob = result.outBinds.lobbv[0]; + + lob.on('error', function(err) { + should.not.exist(err, "lob.on 'error' event"); + }); + + inStream.on('error', function(err) { + should.not.exist(err, "inStream.on 'end' event"); + }); + + inStream.on('end', function() { + streamEndEventFired = true; + // now commit updates + connection.commit( function(err) { + should.not.exist(err); + }); + }); + inStream.pipe(lob); // copies the text to the CLOB + } + ); + }, + function clobstream1(callback) { + var streamFinishEventFired = false; + setTimeout( function() { + streamFinishEventFired.should.equal(true, "stream does not call 'Finish' Event!"); + callback(); + }, 500); + + connection.execute( + "SELECT content FROM oracledb_myclobs WHERE num = :n", + { n: 1 }, + function(err, result) { + should.not.exist(err); + + var lob = result.rows[0][0]; + should.exist(lob); + lob.setEncoding('utf8'); + + lob.on('error', function(err) { + should.not.exist(err, "lob.on 'end' event"); + }); + + var outStream = fs.createWriteStream(outFileName); + outStream.on('error', function(err) { + should.not.exist(err, "outStream.on 'end' event"); + }); + + lob.pipe(outStream); + + outStream.on('finish', function() { + + fs.readFile( inFileName, { encoding: 'utf8' }, function(err, originalData) { + should.not.exist(err); + + fs.readFile( outFileName, { encoding: 'utf8' }, function(err, generatedData) { + should.not.exist(err); + originalData.should.equal(generatedData); + + streamFinishEventFired = true; + }); + }); + }) + } + ); + }, + function clobstream2(callback) { + var lobEndEventFired = false; + var lobDataEventFired = false; + setTimeout( function(){ + lobDataEventFired.should.equal(true, "lob does not call 'data' event!"); + lobEndEventFired.should.equal(true, "lob does not call 'end' event!"); + callback(); + }, 500); + + connection.execute( + "SELECT content FROM oracledb_myclobs WHERE num = :n", + { n: 1 }, + function(err, result) { + should.not.exist(err); + + var clob = ''; + var lob = result.rows[0][0]; + should.exist(lob); + lob.setEncoding('utf8'); // set the encoding so we get a 'string' not a 'buffer' + + lob.on('data', function(chunk) { + // console.log("lob.on 'data' event"); + // console.log(' - got %d bytes of data', chunk.length); + lobDataEventFired = true; + clob += chunk; + }); + + lob.on('end', function() { + fs.readFile( inFileName, { encoding: 'utf8' }, function(err, data) { + should.not.exist(err); + lobEndEventFired = true; + + data.length.should.be.exactly(clob.length); + data.should.equal(clob); + }); + }); + + lob.on('error', function(err) { + should.not.exist(err, "lob.on 'end' event"); + }); + } + ); + } + ], done); + + }) +}) \ No newline at end of file diff --git a/test/dataTypeRowid.js b/test/dataTypeRowid.js new file mode 100644 index 00000000..ba56665f --- /dev/null +++ b/test/dataTypeRowid.js @@ -0,0 +1,127 @@ +/* Copyright (c) 2015, 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 + * 39. dataTypeRowid.js + * + * DESCRIPTION + * Testing Oracle data type support - ROWID. + * + * NOTE + * Native ROWID support is still under enhancement request. + * + * 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 - are for other tests + * + *****************************************************************************/ + +var oracledb = require('oracledb'); +var should = require('should'); +var async = require('async'); +var dbConfig = require('./dbConfig.js'); + +describe('39. dataTypeRowid.js', function() { + + if(dbConfig.externalAuth){ + var credential = { externalAuth: true, connectString: dbConfig.connectString }; + } else { + var credential = dbConfig; + } + + var connection = false; + before(function(done) { + oracledb.getConnection(credential, function(err, conn) { + if(err) { console.error(err.message); return; } + connection = conn; + done(); + }); + }) + + after(function(done) { + connection.release(function(err) { + if(err) { console.error(err.message); return; } + done(); + }); + }) + + it('39.1 supports ROWID data type', function(done) { + connection.should.be.ok; + var createTable = + "BEGIN \ + DECLARE \ + e_table_exists EXCEPTION; \ + PRAGMA EXCEPTION_INIT(e_table_exists, -00942); \ + BEGIN \ + EXECUTE IMMEDIATE ('DROP TABLE oracledb_row'); \ + EXCEPTION \ + WHEN e_table_exists \ + THEN NULL; \ + END; \ + EXECUTE IMMEDIATE (' \ + CREATE TABLE oracledb_row ( \ + ID NUMBER, \ + RID ROWID \ + ) \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO oracledb_row(ID) VALUES(1) \ + '); \ + EXECUTE IMMEDIATE (' \ + UPDATE oracledb_row T SET RID = T.ROWID \ + '); \ + END; "; + + async.series([ + function(callback) { + connection.execute( + createTable, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "SELECT * FROM oracledb_row", + [], + { outFormat: oracledb.OBJECT }, + function(err, result) { + should.exist(err); + err.message.should.startWith('NJS-010:'); // unsupported data type in select list + callback(); + } + ); + }, + function(callback) { + connection.execute( + "DROP TABLE oracledb_row", + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + }) +}) diff --git a/test/dataTypeTimestamp3.js b/test/dataTypeTimestamp3.js index de16cefd..2913a2c0 100644 --- a/test/dataTypeTimestamp3.js +++ b/test/dataTypeTimestamp3.js @@ -98,7 +98,7 @@ describe('35. dataTypeTimestamp3.js', function() { ); }) - it.skip('supports TIMESTAMP WITH TIME ZONE data type', function(done) { + it('supports TIMESTAMP WITH TIME ZONE data type', function(done) { connection.should.be.ok; var timestamps = [ @@ -126,12 +126,14 @@ describe('35. dataTypeTimestamp3.js', function() { [], { outFormat: oracledb.OBJECT }, function(err, result) { - should.not.exist(err); - // console.log(result); + should.exist(err); + err.message.should.startWith('NJS-010:'); // unsupported data type in select list + /* + console.log(result); for(var j = 0; j < timestamps.length; j++) result.rows[j].CONTENT.toUTCString().should.eql(timestamps[result.rows[j].NUM].toUTCString()); - - done(); + */ + done(); } ); }); diff --git a/test/dataTypeTimestamp4.js b/test/dataTypeTimestamp4.js index 6c43f897..47772011 100644 --- a/test/dataTypeTimestamp4.js +++ b/test/dataTypeTimestamp4.js @@ -98,7 +98,7 @@ describe('36. dataTypeTimestamp4.js', function() { ); }) - it.skip('supports TIMESTAMP WITH TIME ZONE data type', function(done) { + it('supports TIMESTAMP WITH TIME ZONE data type', function(done) { connection.should.be.ok; var timestamps = [ @@ -126,12 +126,14 @@ describe('36. dataTypeTimestamp4.js', function() { [], { outFormat: oracledb.OBJECT }, function(err, result) { - should.not.exist(err); - // console.log(result); + should.exist(err); + err.message.should.startWith('NJS-010:'); // unsupported data type in select list + /* + console.log(result); for(var j = 0; j < timestamps.length; j++) result.rows[j].CONTENT.toUTCString().should.eql(timestamps[result.rows[j].NUM].toUTCString()); - - done(); + */ + done(); } ); }); diff --git a/test/dbConfig.js b/test/dbConfig.js index d378d2e8..0997e9cd 100644 --- a/test/dbConfig.js +++ b/test/dbConfig.js @@ -31,8 +31,8 @@ *****************************************************************************/ module.exports = { - user : "hr", - password : "welcome", - connectString : "localhost/orcl", - externalAuth : false + user : process.env.NODE_ORACLEDB_USER || "hr", + password : process.env.NODE_ORACLEDB_PASSWORD || "welcome", + connectString : process.env.NODE_ORACLEDB_CONNECTIONSTRING || "localhost/orcl", + externalAuth : process.env.NODE_ORACLEDB_EXTERNALAUTH ? true : false }; diff --git a/test/dmlReturning.js b/test/dmlReturning.js index 4f52f0d5..d8fa2a71 100644 --- a/test/dmlReturning.js +++ b/test/dmlReturning.js @@ -157,7 +157,7 @@ describe('6. dmlReturning.js', function(){ { autoCommit: true }, function(err, result) { should.exist(err); - err.message.should.eql('NJS-016: buffer is too small for OUT binds'); + err.message.should.startWith('NJS-016'); // NJS-016: buffer is too small for OUT binds //console.log(result); done(); } diff --git a/test/examples.js b/test/examples.js index b30cc0fb..2a318494 100644 --- a/test/examples.js +++ b/test/examples.js @@ -510,17 +510,7 @@ describe('3. examples.js', function(){ describe('3.7 plsql.js', function(){ var connection = false; - var proc = "CREATE OR REPLACE PROCEDURE testproc (p_in IN VARCHAR2, p_inout IN OUT VARCHAR2, p_out OUT NUMBER) \ - AS \ - BEGIN \ - p_inout := p_in || p_inout; \ - p_out := 101; \ - END; "; - var bindVars = { - i: 'Chris', // bind type is determined from the data type - io: { val: 'Jones', dir : oracledb.BIND_INOUT }, - o: { type: oracledb.NUMBER, dir : oracledb.BIND_OUT } - } + before(function(done){ oracledb.getConnection(credential, function(err, conn){ if(err) { console.error(err.message); return; } @@ -537,6 +527,19 @@ describe('3. examples.js', function(){ }) it('3.7.1 can call PL/SQL procedure and binding parameters in various ways', function(done){ + var proc = + "CREATE OR REPLACE PROCEDURE testproc (p_in IN VARCHAR2, p_inout IN OUT VARCHAR2, p_out OUT NUMBER) \ + AS \ + BEGIN \ + p_inout := p_in || p_inout; \ + p_out := 101; \ + END; "; + var bindVars = { + i: 'Chris', // bind type is determined from the data type + io: { val: 'Jones', dir : oracledb.BIND_INOUT }, + o: { type: oracledb.NUMBER, dir : oracledb.BIND_OUT } + } + async.series([ function(callback){ connection.execute( @@ -570,6 +573,53 @@ describe('3. examples.js', function(){ } ], done); }) + + it('3.7.2 can call PL/SQL function', function(done) { + var proc = + "CREATE OR REPLACE FUNCTION testfunc (p1_in IN VARCHAR2, p2_in IN VARCHAR2) RETURN VARCHAR2 \ + AS \ + BEGIN \ + return p1_in || p2_in; \ + END; "; + var bindVars = { + p1: 'Chris', + p2: 'Jones', + ret: { dir: oracledb.BIND_OUT, type: oracledb.STRING, maxSize: 40 } + }; + + async.series([ + function(callback){ + connection.execute( + proc, + function(err){ + should.not.exist(err); + callback(); + } + ); + }, + function(callback){ + connection.execute( + "BEGIN :ret := testfunc(:p1, :p2); END;", + bindVars, + function(err, result){ + should.not.exist(err); + // console.log(result); + (result.outBinds.ret).should.equal('ChrisJones'); + callback(); + } + ); + }, + function(callback){ + connection.execute( + "DROP FUNCTION testfunc", + function(err, result){ + should.not.exist(err); + callback(); + } + ); + } + ], done); + }) }) @@ -900,8 +950,155 @@ describe('3. examples.js', function(){ }); } }) - }) + describe('3.11 refcursor.js', function() { + var connection = false; + var script = + "BEGIN \ + DECLARE \ + e_table_exists EXCEPTION; \ + PRAGMA EXCEPTION_INIT(e_table_exists, -00942); \ + BEGIN \ + EXECUTE IMMEDIATE ('DROP TABLE oracledb_employees'); \ + EXCEPTION \ + WHEN e_table_exists \ + THEN NULL; \ + END; \ + EXECUTE IMMEDIATE (' \ + CREATE TABLE oracledb_employees ( \ + name VARCHAR2(40), \ + salary NUMBER, \ + hire_date DATE \ + ) \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO oracledb_employees \ + (name, salary, hire_date) VALUES \ + (''Steven'',24000, TO_DATE(''20030617'', ''yyyymmdd'')) \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO oracledb_employees \ + (name, salary, hire_date) VALUES \ + (''Neena'',17000, TO_DATE(''20050921'', ''yyyymmdd'')) \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO oracledb_employees \ + (name, salary, hire_date) VALUES \ + (''Lex'',17000, TO_DATE(''20010112'', ''yyyymmdd'')) \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO oracledb_employees \ + (name, salary, hire_date) VALUES \ + (''Nancy'',12008, TO_DATE(''20020817'', ''yyyymmdd'')) \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO oracledb_employees \ + (name, salary, hire_date) VALUES \ + (''Karen'',14000, TO_DATE(''20050104'', ''yyyymmdd'')) \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO oracledb_employees \ + (name, salary, hire_date) VALUES \ + (''Peter'',9000, TO_DATE(''20100525'', ''yyyymmdd'')) \ + '); \ + END; "; + + var proc = + "CREATE OR REPLACE PROCEDURE get_emp_rs (p_sal IN NUMBER, p_recordset OUT SYS_REFCURSOR) \ + AS \ + BEGIN \ + OPEN p_recordset FOR \ + SELECT * FROM oracledb_employees \ + WHERE salary > p_sal; \ + END; "; + + before(function(done){ + async.series([ + function(callback) { + oracledb.getConnection( + credential, + function(err, conn) { + should.not.exist(err); + connection = conn; + callback(); + } + ); + }, + function(callback) { + connection.execute( + script, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + proc, + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + }) + + after(function(done){ + connection.execute( + 'DROP TABLE oracledb_employees', + function(err){ + if(err) { console.error(err.message); return; } + connection.release( function(err){ + if(err) { console.error(err.message); return; } + done(); + }); + } + ); + }) + + it('3.11.1 REF CURSOR', function(done) { + connection.should.be.ok; + var numRows = 100; // number of rows to return from each call to getRows() + + connection.execute( + "BEGIN get_emp_rs(:sal, :cursor); END;", + { + sal: 12000, + cursor: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } + }, + function(err, result) { + should.not.exist(err); + result.outBinds.cursor.metaData[0].name.should.eql('NAME'); + result.outBinds.cursor.metaData[1].name.should.eql('SALARY'); + result.outBinds.cursor.metaData[2].name.should.eql('HIRE_DATE'); + fetchRowsFromRS(result.outBinds.cursor); + } + ); + + function fetchRowsFromRS(resultSet) { + resultSet.getRows( + numRows, + function(err, rows) { + should.not.exist(err); + if(rows.length > 0) { + // console.log("fetchRowsFromRS(): Got " + rows.length + " rows"); + // console.log(rows); + rows.length.should.be.exactly(5); + fetchRowsFromRS(resultSet); + } else { + resultSet.close( function(err) { + should.not.exist(err); + done(); + }); + } + } + ); + } + }) + }) + }) diff --git a/test/fetchAs.js b/test/fetchAs.js new file mode 100644 index 00000000..c47997ab --- /dev/null +++ b/test/fetchAs.js @@ -0,0 +1,260 @@ +/* Copyright (c) 2015, 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 + * 56. fetchAs.js + * + * DESCRIPTION + * Testing driver fetchAs 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 + * + *****************************************************************************/ + +var oracledb = require ( 'oracledb' ); +var should = require ( 'should' ); +var async = require ( 'async' ); +var dbConfig = require ( './dbConfig.js' ); + +describe ('56. fetchAs.js', + function () + { + if (dbConfig.externalAuth ) + { + var credential = { externalAuth : true, + connectString : dbConfig.connectString + }; + } + else + { + var credential = dbConfig; + } + + var connection = false; + + + /* preparation work before test case(s). */ + before ( + function ( done ) + { + oracledb.getConnection ( credential, + function ( err, conn ) + { + if ( err ) + { + console.error ( err.message ); + return; + } + connection = conn; + done (); + } + ); + } + ); + + + /* clean up after test case(s) */ + after ( + function ( done ) + { + connection.release ( + function ( err ) + { + oracledb.fetchAsString = [] ; + + if ( err ) + { + console.error ( err.message ); + return; + } + done (); + + } + ); + } + ); + + + /* Fetch DATE column values as STRING - by-Column name */ + it ('56.1 FetchAs - DATE type as STRING', + function ( done ) + { + connection.should.be.ok; + + connection.execute ( + "SELECT FIRST_NAME, LAST_NAME, HIRE_DATE FROM EMPLOYEES", + [], + { + outFormat : oracledb.OBJECT, + fetchInfo : { "HIRE_DATE" : { type : oracledb.STRING } } + }, + function ( err, result ) + { + should.not.exist ( err ) ; + done (); + } + ); + } + ); + + /* Fetch DATE, NUMBER column values STRING - by Column-name */ + it ('56.2 FetchAs NUMBER & DATE type as STRING', + function ( done ) + { + connection.should.be.ok; + + connection.execute ( + "SELECT employee_id as SEMPID, employee_id, " + + "hire_date as SHDATE, hire_date FROM EMPLOYEES", + [], + { + outFormat : oracledb.OBJECT, + fetchInfo : + { + "SEMPID" : { type : oracledb.STRING }, + "SHDATE" : { type : oracledb.STRING } + } + }, + function ( err, result ) + { + should.not.exist ( err ) ; + done (); + } + ); + } + ); + + /* Fetch DATE, NUMBER as STRING by-time configuration and by-name */ + it ('56.3 FetchAs Oracledb property by-type', + function ( done ) + { + connection.should.be.ok; + + oracledb.fetchAsString = [ oracledb.DATE, oracledb.NUMBER ]; + + connection.execute ( + "SELECT employee_id, first_name, last_name, hire_date " + + "FROM EMPLOYEES", + [], + { + outFormat : oracledb.OBJECT, + fetchInfo : + { + "HIRE_DATE" : { type : oracledb.STRING } + } + }, + function ( err, result ) + { + should.not.exist ( err ) ; + done (); + } + ); + } + ); + + + /* + * Fetch DATE, NUMBER column as STRING by-type and override + * HIRE_DATE to use default (from metadata type). + */ + it ('56.4 FetchAs override oracledb by-type (for DATE) at execute time', + function ( done ) + { + connection.should.be.ok; + + oracledb.fetchAsString = [ oracledb.DATE, oracledb.NUMBER ]; + + connection.execute ( + "SELECT employee_id, first_name, last_name, hire_date " + + "FROM EMPLOYEES", + [], + { + outFormat : oracledb.OBJECT, + fetchInfo : + { + "HIRE_DATE" : { type : oracledb.DEFAULT }, + "EMPLOYEE_ID" : { type : oracledb.STRING } + } + }, + function ( err, result ) + { + should.not.exist ( err ) ; + done (); + } + ); + } + ); + + /* Fetch ROWID column values STRING - non-ResultSet */ + it ('56.5 FetchInfo ROWID column values STRING non-ResultSet', + function ( done ) + { + connection.should.be.ok; + + connection.execute ( + "SELECT ROWID from DUAL", + [], + { + outFormat : oracledb.OBJECT, + fetchInfo : + { + "ROWID" : { type : oracledb.STRING } + } + }, + function ( err, result ) + { + should.not.exist ( err ) ; + done (); + } + ); + } + ); + + /* Fetch ROWID column values STRING - ResultSet */ + it ('56.6 FetchInfo ROWID column values STRING ResultSet', + function ( done ) + { + connection.should.be.ok; + + connection.execute ( + "SELECT ROWID from DUAL", + [], + { + outFormat : oracledb.OBJECT, + resultSet : true, + fetchInfo : + { + "ROWID" : { type : oracledb.STRING } + } + }, + function ( err, result ) + { + should.not.exist ( err ) ; + done (); + } + ); + } + ); + } +); diff --git a/test/fuzzydinosaur.jpg b/test/fuzzydinosaur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9b8907d07214e7914e52e6da37606b51a48236f8 GIT binary patch literal 11981 zcmd6NcT`i`)^7k2DWV`vP~Zqi6)Xfo3y1;|q$Pyj6cIwC6M9v#P?b=WB2q#~LO@FB zO%xCikS?J~hajPbD)`|!_nvd_dH1_xyno(!Gh-xKJA2Rh+rPQ?nrqIPpI?7|1Du9v z>uLk&=;#2qj$VMDBY+!#)2wXlY%HhQPqK4zoIb-R!OzFT!*@kQ?7W2XH8oYGYam69 zTh;~|H_fyZK}N2|W_EX-oSne>o&g^B{H-0G9DXUG&zvMU*-7!XbhNB;Ep9av=G0-zGFflPR zGBGe7okK^@aEycTIOlIy&RjQUx^Txc;$<<{)oW0%toPhJKuws*-RvF_QTc>PF$eF5 zB~7dMa@KC#g!`b6v~m5LmEZ0CI?C~{w*NRv2Cy+4!Qx=x0H_1r{e|rRn{UT6=5jQ< zT)1roEVLVDm(&ZHw3BWPV)$PK#3UPk{|8K$@d@LShe?P#;BdsD$JYbzz%FXWtl%K} zvv;_{f4>zr#mE=ZW3&05c7@DdQSZ;k{uoV|vbRXPzi5qkVKLABsO0E+dq@B9s{pzu zy%r%S1Zn-1SUyeb?8YeVEmusk`MzusGN)ov^~l%%MVPwk%)ely5*A*5OrFm=3#ybU zx+S2%KPx~UONZL_c^ebInh)U8?+Tlw(moK3JEGJ4b)?3_$Qbdq?RVleW=6i<5%o6A zqoP=Dv7WbTfa8E~)bob#o;n$rBkMTqx&-26d`r3um=(qcY#zV}suknI7Z`-uZL0!_ zuKb2wj*f*xsQKnMt01@BlGP5yHjJ*xm!-AZ)geS+r5TOh{=Yy!E+l$P&u8fc#ua+@ zqnVtAot_cOrM&dqP6YW&Q3gN7V&&!sFBj&8m!82CI=C4@I(2!vNHn%4_9{k2%a*yU z-jBwxk~2}MuW86>BlH~MY}Qj%`q*kCfuR5(B#_!3SxW)WFuE5^KB( zqvMB|-8Ds8r$tv+MoF@*TBDad*#K^%U8R#iOBTB%X6W9N!hUbi{Pao7CR4wwi6T(} zvvG5NTjf{jxaER>2&5Hjd5#oRqu`Up?U!_d#dVV@Ig|@NL9}^9k6(OlF>nK$hQy?P?xGPqKGSy;h2t}i{yurM>DUu=P-M34=%*x2SB~Z6m2Fmz1(?74Cm5;o>y;FWUQ3F zm(i^gwhO)2MnvCXdS2Kqm+hyow{Kq-7cjK(Zslxp=d=({#A_FDO|8EYl5VGv$#8*% zsA-!zKi-oS9knN#E&6LKWBK)>uTDNT3%Yc&xMDozEj$O_f+|$qm9O@iY1sI9LVLEy zrKdi_+#|oEAkbSW^*+JQ0AD<>qdK)8p6Ft=sLT>sCvJ>+s<7J7|9qUns~ig_gKHB- zzOc89jKGTubD)&;EXXAwvs~uk<9Tm#(-2}zlUe3QCCjr?)rsP+i~C%?u*Nx7M9y#Cy8ri&jJtw;f%o^8u}HY?$q-OuP$UPq&CMB;QK z?tL|H8cdy?KUcSA3>#6jSZSiqlvxq?JY2N3m6E2?-}_(qqkFQNMo$>swv(tWoY~+S z=1DnazPZYflSS$4rFsu0rcSNdCYr~4R41SIN%lTBuV%zy1B0WzrV{jbN))}l$%TC7 z>+2KG&a8-6)8{-Ox%7S}G`E~?y)|D{aqQa(o;8y1UrU;Or|9Yy}hn0iADAZ*n2730Aa3nv%%!6 zp)=5B2QrhDF3B}-dS36Fr847V*YA@`a!PMij#;&f7MIV|y68+X8d=sGX2lA6ttHrb zaQ>%o%n(tD(zP(S{5aLWTUaDv%#Xw0JEis|g8v7y@39LFS*Wq14e^9Vw>!N(UbpP1 z*-O;k(~8h{A&p{y6H1ET<(FNhZQ6}<1ZBBX78vHCi8y)8K*99Lqs6a7h&!_m(p^-B zyREls8Va%?l1>G=yBFS%1WENa`)>Ed4wzO+wsyw9WA+i@)6(WJj+#j@zxC;~p+v=M zQo*JE>iRdLoV`;DYnhy`YR3WPIR%q((MXRt%(!E%i&|Cp{$RBcM8 zF)i#1``QbUv_9k5@~0?00*;+&d3Go1_64e2LR6D`!m5V8!r_~xF&&9_i_MxW(%I$= zJ*U@Ssychb$KGrtL_68*@Uw(o{c)Pp<3CJA`r7ddx%FCH!EW)b6S09MbDt3vG}po$ zaj5mxnL&+r@~&>VCJvIb{#UIu%aFaW=$Xd7m&;=T>p`IRVDH;3d56|!e5*H0k%&=# zo6e6pKLI_7;n6N(hC-z~C0b=ZfyznivA#QSSs7s&q>XG>FGnmtN7VCjKrA)3^9H0M zL)kl~kbmRO8}|bMfpk)H+PHAZy8HRISeu-2O^!2)#PTlj+qBmH3}X$(lAxv>d!kuO z6Vyai1ndr_FtQ|s^gPI3!7EXCxzEf|+GYbA*W6t_{me?sbD$)h?kDNinN^%bUVDln zJWCBCH8>$zStYI{cPz!wna74m6i{*T-P)WsNCG}<_7gI?>Q69+VkO%qMH#JXQM!Yf zcdJ@*T=)5vEEx-ytk)bmgJ19@M`8&s)?U`+8DGHZZi#VJ(h2aPIF-J{Mlbq)@Aonm z^Mw(H+W){b1EXCvWo=0jZAa+)8vYz#0-JINmv#iWP78*sDC>XkzjARTnqug?g@1?Z zvVO(+q&F_;+40r7UH#9$@y@jhUnkU)*Yag%bL+%#NOx8njH&rYjP49e12mvWs6;HVLxK+-s(s-}dC#RYu4ZUij(M7V9<7`nKR^}<3;UABSgH=p+Ol=AHr2ojeKWgn zFVcX|4k2B^KkSQ0K@&=y8}5l3yUVOhVFk?&Qb&{H!jm*_3LewH{=9UegfXN_kEYLGc|go>y&11 zsp5SrwoG_5_^S@9XbfAph{}?Bf>=C3F2bY-zt9hhzkDeq4KZv{{dISYSpsleNRzI* z2xB?fnjKAl(kHPn50^LMPb(Q7_Ptgzvb9EJ=O-%kQA)mY0sv3l7uxMaTi56;hkgRi zz?QF0krAyL6^2FX&(6R2J9|fCA?08)^WwrE( zy#+a=+oE?8yvb-$U5>I5yjoL+CL)?QJm+DyncRz@YFFPONAXVYXXhd0$B80r+vij$ z2P8MXA79sPlNnRql`VYfS+s6XIYzm-KK)ee4c5>%G`w;vSOW)CU*p(ko$6p{oA@%c ze|d*aUMcNKkwf-qf7cbkw=iB*s4+@8nwELNrc_47s{WiJ-<@*Cq!amC+S1H|;d~4c zwUYQ$V89P5i`Psh7|94`BxJjtRA|*K!z%CMaawI+6ORULmXJa@Ignf?x0YRG=UIBlQw}P2MFD1>1m(NzRSnC@X*8r#rTWS zwjjsnC%@y~YbsbL?0pOTu@_bqrbJbl5+XXu-3uq@)PBcqESf}gjqwwcv+iNE@zYpl z`OG)~F{cX&7VF6xwSx8`pv#N1PAQxWgtIMrR}w<=0{??MPM*JC{*=htJ!luSV5;UT#ShjlR0!uGQXo?^`_jX3L~N`vnD!4X8r^_yxA7W@^U5a z_JXg=Pk>2|yLfwUqSb0gOX#ZXf~Nc=pQhB6C|$80k(V{<8rDHzVUs|#!R(#QsN3&? zGY(GvzJ1eFtFLST%I;jtRKz~z{KU<5VIIgwdr-1<%t0tAkjqpf(b`3!ylpGV40kS? zID*iTN_SLX;#>-Xqg~Kg!@-{R$}!%gGhTI*c1?{lc1zr{5?$<51*0`1GhTqoQy>SkV4Czc8)A;m%a6e70&Uo|4bFpc^y{TMy;#cK zyfW!eKc-akFcXJ6ub4xzPCPEczY`oXF5 z`lSrQZpA=N$JAuH=jze>6&sg@Quhq06Q5iEeb{}fskUqZIqFk?KnR+YzI^ zv|C%zPu!)e&>2D&kEgPrwqI0w5}nR9&kBbB-ESiMgTcxoc9sb49 zwy8QX%S2p9js|q(rN`3tJ7dpp`O{AqX3;AZq}_#(0$g)D6vovZ5EBe&EH>sSKCuIQ=;H;Kai4f}a`ZJj8!BYL4mt(7n;2R&M2D+vJxM zedXLlv9x%#2I6P-<-C#JWkJNqjp$Y;gA9I?m+ya6nSe3f;8YP>ylvrr8(u$3wXL)_SWLeuu^dQ-J=Ej{CO2C0#Y77 z=dre;VYqqe{ZSe^<>C=scQZTb*1*F=K^Vi=e;WD6Cq1w}>}H?c3Wv3e-hTD|EmnrM z&mbSBDT;y~fRFy<>?y7IPdJK69~z?y*Y);9FJ<1?c_A%bUCw)js7nG>w#&WGOSQKy z=O0U&dwEi85DSTCcWM@!Q$MarE0RFreG`4~Qt5$G8*}6}y_%61zX6xvl|(5L1%lC4 zZHERtSmUcw)l&Fz`aG*X&(>lgf3W2Cr=&#zk*F&fO&u%l*munOdBepOdQt3#gD7Wm z&)Z^1>N6qOKgnWxZt@uHlRzu&ZkIS56kJtnf#_X96kT69Do?Q=pCfPW%?gU{OTUu4 znr*QK95=Nre-xJ6`6D}%>&)IQZ@UGi4+i~G=4R9Jpoa8lrt3q~Agz{QBYQpP(LSO+ zL=Lz0cfPLGzmZS1TQh4c0J6lPc7n32>F$4L{E!V%TYCeZG6K+bjJC_UQlyN%I%`)G zR$Aq1b0>1?Xj$&i$$G{%l)ayf?{{iIO9te5@Ep2nn=?ZMp(&}`d>#B&cUhi*G^w;t zv-v)*D;xe-R>&k|wHxVB=%UDK;uiZk;i38DQ8~fe-Mwn0oeKzBg9X=^ZwL0+f3+i1 z6ex8W&VzZo_vLaJjuf0!*=JEK!?3(Gls-3PB;6V0-0~=W`OM=KAp4xQt1J>RWbq-| z@XVtX`he`EwWw(Lndr@gC96H&%Ld#cnQRede}VX&2a%v9)*I9Q8)D7f{U)V2sibpr z>tA`O9oXT;tA~>FR8e8X1SOgO0jS7r(Rc*{ojcGYTu|;#gve()2$LM(Cl`@Ck` zbZq^!IzwxG(pZ3r-dtbQN3-H!i*6IVl1DyRj*V{MU5}HeL?LsRJMHfCuE?@1eW{eP z{=3yM8@mE#M=6tqK9fbw%MTY4n$*PN{)H$2T~_{Z7v)q0W=Z;zJ92cy3}=dl#w&s9 z-|B!IlPhh_-lD(Tcv;$JsUQk^Dwn!*bbkWoD_Ozht=P{nEHLvIXMi2+5m?oun4z*q zRQ*zeTnwo73D##Qtl)$V3(05cHH>gpMisb|KomD=Wy_Jh%}mYk6Cn6^fPIzH!vCBt zU|vO&<*a7oeL%EJ4VnBH14*++)~UWE?^o9$?($xH5^{-dEw0Wl{m>{mY)YWK20xXU za=tfVvqSinj9+Z-j)SLdVz3>@SF30p5APosmjsIdd?}UwxBBhlukp_P3+A>`BnKOa zv2ntNryTe6L;7QBy5mD+gG__o*gBTyC59Z1cfR8WUNZTrJjI}l+ zqAcNsQe8v~RQhc40k$RxF&Jj5wj9x4QW}DFayLLp=~c;;-`rfza$ zBH5fgeB%i@Msn}SuX+CxP5DC3@>UN~C9DlHxdPMyY8Gz%B)2S3iUB(`+Tn#&RjG)(3P?q0-hHj8Q;&tZ?e*)Bw?=T4e6F&fOXVcN4dLEh-*md?c zJ5PMRL2SQ>k|NKp;%G-0q{_@xetsh6vZ4n6wt|nFL)6QyBU6w%rrLK|c|4RqVYfu%I#=N);Yo4R zftsjcc}pc8`Z#T7uju%(t|arL=wS;}3;zQcn?gbdLlP0mqhJcma9PuD&aK{%ItiJz zu~BNxyBxl_9L&q5cJKnjG8Tim}Jl4I_RbX5FVECwBE0kKTNDbRBJ;dg_#`32p<~Z^U4W_4V zD;ytC6F0z!N%<=DBrFVwCA!i5d!l0>vuX^(4t$UAsEguiEA+tlE#MJ#`3uSOsdw6nj?NB6_BedawE)r2Y|->86O5K9Ue3q9p@V&<^$2 zrQ;h=oO_ATCs?#oi+t=;ZMW*r{uWrc-jCu(Ny->u!3&D%fsOG>N+{R8&S<2L#his+ zBlDX9l26g1u1?AYWWi5B_$zMHDpLyY$=-}xeJh@>-YO#(NP!7?O9t*hB}bZudE@W? zWCI-6_`|VBkKE^ZeorrS(@j5*C7yW04AGIanQ;8d-6TJ;z0o$)54DMM*?2w4igOY7 zX;%UBOFdIxR-hJ#Z4tM`pQ0C8XurvPK-1_pcU`*&4^6;U-n51!Ix2tCjGzO&2&z3> zKRmBuhp$Iw43c~U z(V9M)OT6Z-EMA|@Iq`52Qx@ZZ+rNT28(rHFZ6t)h9qs~_+>9|Z{lSuBHdI85B957* zIHlu_{iC+%aN2_y$HavAXvIZ@s_xxu`3IlaGv1J^2~S!5xTl+R%ZOQxiB!=P@GE*l z1a^e1CFRqLtaag8uOcOZqZsJE3SC1iWOUsPWP z&}~$uFxJl0B;O)Yzu{b+EX#PgvUvLK{P~7!vI|L;B-Z>UZu`oV;~qn0DdE4a`GtLh z;0cN9guIL=rjOJ$JGi}(l5Ei6P;&_AOT?V;RGbu?5(&T$Rp-yX;W{4ereD5(Ag<>= z`Xkdgda5Nbu(9epMXJ0k?~Q-<>n(ok;yBkL`U1p<^bLDf`f&03kc!`wUYd_wlXXloP#E=^xYlOc@iYImmJ2#MZvF-{3 z1cQ-OSB^4YH3vic7AlfH)-gw{y-EEEfMSbtrlqByRXm&Z&tt0QCJCUTxMdMVlW6T_ zwQfRR3}T=X*E{ik-46ASAWsz)mQ+MAq>hwie)~=kge10Pm+T{3wU#?|~Es?ZQZ`>F(BAXrDo0Z=d+a9f`nK{J}`{A$p(p_LN(;N3n;_U(Sr)IA~zZVcUchte9%i;@B ziz{IOVTI(boavNKyMU`?OGkm{5RB6YK5%ejvgv@&vecu81VNj&m$6nsngNmg&*diR z741Cw3r4&ND?b4+tM%rayS{JgFN0r>@*3@y;_KjNH{%u>;vuw#!|;%-S`x^I2pf?( zWE!*@f#M>SOQY7J8(CCZ4l}gG{H)Ylc2cS`I`o&l6YlaF2a} zwOWx?KJS%~gD`t&UFN54&{7kldO<%*)_%h0%65tOyFzbq-TJbe_`CEEdJ_eUNM@suG(SbdRM+TG?^(SAKohQKIkN(%R3l~c8%C7EyV@d%1cyMm(KF34~8B~ zF<1}7S+~Yfb1yl>Wh&eG$<>}9HOn^55qUO-J6pjZf5k0IOR2l+!MV!taufmRcPIV6 zv6fPPuMd!AlibQSA*aFVnu1q`i|EdO_NV1ptR|#$rD%t5YAa&q86Rcwc1{b!%cB*q zY$a=dEc+Je(rdkU@z}>-k0etUQ34*%HkAq*+V_fVu~F4(I1MthQl=;+Nxt7>=fPx; zjTFH1j)jsM*jD?Z6@3EPGuqQa0)%Cyg;wRkO-FxxVDy{S?jb}AG$&)2p`kIVMbX=I z%sJlB80)*cCNf&ty4UAYg&@XtL)vLD ze6e>T5F?+Mno=b6Wzk~yCQfM0L|`9u)c3&%Nu*j2N*Nb1Ek?DDc4tt3^inV3jbo(1 zAX0L8ksP6As)38=Le&MUQt2(rq-tt9bU(c0)BU}QW=rk_N~*n?Vl?m7P}QcS&+n!v zz_IXa2P+3F<3b%O`|h=D!O2tBJ9g?kA-F|9pt4S8xIn)`Ao)Hui~ac}RlF%B({}wixzjUR_E%Y&YETViY4v z)a;Q&2EAP~L#ni){R{Prs&f9kSEGFT?IMg%ub5)~nC zS6j>?E{>Fbr?H+5<1*qY{xFt2R-g7U*&2YO@w;(nPXwxrSlz-BfM?BoR;wLqnIq4? zb~;`INyr>^*Fi2``3YDqT|zUg7;5xM*0%I!lJ1cslZfiC-3`F`5!EUb zRLR(3z2eanp9!Co1Bp{lp3I(yCc%3-=MEeP#M4ZVpsvy<4T^k#tDn8K`~ZIihaX;( zp@p5kq*=V4UR-1q=$di>r3dDPsMdE+S*4j8Se~p2&3q=1nlbZ$C=S&ZkJGwAv{fGB z7E7;sr?yPcu{`8N*4bCt7oR9P_G>rrr!+Eg?O~`_J_zCK8lAF!PY;BoaUNE1H1*LA z-7OcC7<65?UN1{>SiTY!#Wl_!W2^CciH5Kww-}@gpj+i(L!_~RS(i?SI9JTKnio6@ zFG=6!3I@!Si$65!rmYPJdIfmn58j#u<#qA!4_I<-d=PxCP=@m`ElYV6O+Om_8MY}T zK!K1l%z^365k|-t-$$q8(_lhpa!RC?&B_J?AbDVbW#jLFe`Qn)u$n>7H}5549YgrNvj%xJz^&){pT%OxYlc$&#e>^O>gR`vibrr=IRD??j4C5=jJOeD6E~-df2^<3pzI;UADy=TLIYUZ= z#0Hd9Od9s)Zmel&ZFXZ*hJWa}BM!qWwti?lU4f)qcHTRDz`4V59?XuJ;l;k?Y=df`~3~%Fp zgrQqr60K@}hnOSb9P#Ocxu74%gi!JZr0Y^~XPPhb2~9 zD=$lBT1MH<{RH4Xm7ME8zt+8_MjC%EHXpN5^73XL$ucD z$Nn%MymX^12NdG?P%ePvGU(cu0bF88-O^uA^Tg?li;(p_g$*{qt{H&QH#eA$8{Q>3VELE&CBvl z+YC114H5K_Y?!oTI9lcZAq;>&9~t_x^@vvKMQ>MaHNca;{KfgRb0}vw>2@v$2)9dq z^Bpl5zN#r0?k^sqhEdl!^K2>cVufBJS(unftRs8+69hz#c+@sm~mST}ghZV0>is zN?mbOz{d0%{m4Ht0s#Nw_Zxb8K(UE7o|e7?I``$Bh~wj{lS6KHF=mai=3Ym^Y}HMs7K*n9OIqLw`41kEXQ1~V+iF(vWX!YA(5#69;}U(}Vh=7Rla*2wnD#*^k6 zL4K&2#Jj7t7oIwz;21IEgFL1rS9o`1k=<3Tisc4!a&5JVhSi@S0E{8C`T={$O_QI1 zvJ<{(p!X(I{tM=5-X4pC)ml>AOCDWHRC^CaA~geLR#8Ee>`z59bLjDMTdX3s_YRL3+_ 0) + 1.5 Testing commit() & rollback() functions + 1.5.1 commit() function works well + 1.5.2 rollback() function works well + +2. pool.js + 2.1 default values + 2.1.1 set properties to default values if not explicitly specified + 2.2 poolMin + 2.2.1 poolMin cannot be a negative number + 2.2.2 poolMin must be a Number + 2.2.3 poolMin cannot equal to poolMax + 2.2.4 poolMin cannot greater than poolMax + 2.2.5 (poolMin + poolIncrement) cannot greater than poolMax + 2.2.6 (poolMin + poolIncrement) can equal to poolMax + 2.3 poolMax + 2.3.1 poolMax cannot be a negative value + 2.3.2 poolMax cannot be 0 + 2.3.3 poolMax must be a number + 2.3.4 poolMax limits the pool capacity + 2.4 poolIncrement + 2.4.1 poolIncrement cannot be a negative value + 2.4.2 poolIncrement cannot be 0 + 2.4.3 poolIncrement must be a Number + 2.4.4 the amount of open connections equals to poolMax when (connectionsOpen + poolIncrement) > poolMax + 2.5 poolTimeout + 2.5.1 poolTimeout cannot be a negative number + 2.5.2 poolTimeout can be 0, which disables timeout feature + 2.5.3 poolTimeout must be a number + 2.6 stmtCacheSize + 2.6.1 stmtCacheSize cannot be a negative value + 2.6.2 stmtCacheSize can be 0 + 2.6.3 stmtCacheSize must be a Number + +3. examples.js + 3.1 connect.js + 3.1.1 tests a basic connection to the database + 3.2 version.js + 3.2.1 shows the oracledb version attribute + 3.3 select1.js & select2.js + 3.3.1. execute a basic query + 3.3.2. execute queries to show array and object formats + 3.4 selectjson.js - 12.1.0.2 feature + 3.4.1 executes a query from a JSON table + 3.5 date.js + 3.5.1 inserts and query DATE and TIMESTAMP columns + 3.6 rowlimit.js + 3.6.1 by default, the number is 100 + 3.6.2 can also specify for each execution + 3.7 plsql.js + 3.7.1 can call PL/SQL procedure and binding parameters in various ways + 3.7.2 can call PL/SQL function + 3.8 insert1.js + 3.8.1 creates a table and inserts data + 3.9 insert2.js + 3.9.1 tests the auto commit behavior + 3.10 resultset.js + 3.10.1 resultset1.js - getRow() function + 3.10.2 resultset2.js - getRows() function + 3.11 refcursor.js + 3.11.1 REF CURSOR + +4. binding.js + 4.1 test STRING, NUMBER, ARRAY & JSON format + 4.1.1 VARCHAR2 binding, Object & Array formats + 4.1.2 NUMBER binding, Object & Array formats + 4.1.3 Multiple binding values, Object & Array formats + 4.1.4 Multiple binding values, Change binding order + 4.1.5 default bind type - STRING + 4.2 mixing named with positional binding + 4.2.1 array binding is ok + - 4.2.2 array binding with mixing JSON should throw an error + 4.3 insert with DATE column and DML returning + 4.3.1 passes in object syntax without returning into + 4.3.2 passes in object syntax with returning into + 4.3.3 passes in array syntax without returning into + 4.3.4 should pass but fail in array syntax with returning into + 4.4 test maxSize option + 4.4.1 outBind & maxSize restriction + - 4.4.2 default value is 200 + - 4.4.3 maximum value is 32767 + +5. externalAuthentication.js + 5.1 connection should succeed when setting externalAuth to be false and providing user/password + 5.2 error should be thrown when setting externalAuth to be true and providing user/password + 5.3 can get connection from oracledb + 5.4 can create pool + +6. dmlReturning.js + 6.1 NUMBER & STRING driver data type + 6.1.1 INSERT statement with Object binding + 6.1.2 INSERT statement with Array binding + 6.1.3 INSERT statement with small maxSize restriction + 6.1.4 UPDATE statement with single row matched + 6.1.5 UPDATE statement with single row matched & Array binding + 6.1.6 UPDATE statements with multiple rows matched + 6.1.7 UPDATE statements with multiple rows matched & Array binding + 6.1.8 DELETE statement with Object binding + 6.1.9 DELETE statement with Array binding + 6.1.10 Stress test - support 4k varchars + 6.1.11 Negative test - throws correct error message + +7. autoCommit.js + 7.1 autoCommit takes effect when setting oracledb.autoCommit before connecting + 7.2 autoCommit takes effect when setting oracledb.autoCommit after connecting + 7.3 autoCommit setting does not affect previous SQL result + +8. autoCommitForSelect.js + 8.1 should return previous value when autoCommit is false + 8.2 can use explicit commit() to keep data consistent + 8.3 can also use the autoCommit for SELECTs feature + +9. columnMetadata.js + 9.1 shows metaData correctly when retrieving 1 column from a 4-column table + 9.2 shows metaData when retrieving 2 columns. MetaData is correct in content and sequence + 9.3 shows metaData correctly when retrieve 3 columns + 9.4 shows metaData correctly when retrieving all columns with [SELECT * FROM table] statement + 9.5 works for SELECT count(*) + 9.6 works when a query returns no rows + 9.7 works for tables whose column names were created case sensitively + 9.8 only works for SELECT statement, does not work for INSERT + 9.9 only works for SELECT statement, does not work for UPDATE + 9.10 works with a large number of columns + 9.11 works with column names consisting of single characters + 9.12 works with a SQL WITH statement + +10. nullColumnValues.js + 10.1 a simple query for null value + 10.2 in-bind for null column value + 10.3 out-bind for null column value + 10.4 DML Returning for null column value + 10.5 resultSet for null value + +11. poolTimeout.js + pool terminates idle connections after specify time + +12. resultSet1.js + 12.1 Testing resultSet option + 12.1.1 when resultSet option = false, content of result is correct + 12.1.2 when resultSet option = true, content of result is correct + 12.1.3 when resultSet option = 0, it behaves like false + 12.1.4 when resultSet option = null, it behaves like false + 12.1.5 when resultSet option = undefined, it behaves like false + 12.1.6 when resultSet option = NaN, it behaves like false + 12.1.7 when resultSet option = 1, it behaves like true + 12.1.8 when resultSet option = -1, it behaves like true + 12.1.9 when resultSet option is a random string, it behaves like true + 12.2 Testing prefetchRows option + 12.2.1 cannot set prefetchRows to be a negative value + 12.2.2 cannot set prefetchRows to be a random string + 12.2.3 cannot set prefetchRows to be NaN + 12.2.4 cannot set prefetchRows to be null + 12.2.5 prefetchRows can be set to 0 + 12.3 Testing function getRows() + 12.3.1 retrieved set is exactly the size of result + 12.3.2 retrieved set is greater than the size of result + 12.3.3 retrieved set is half of the size of result + 12.3.4 retrieved set is one tenth of the size of the result + 12.3.5 data in resultSet is array when setting outFormat ARRAY + 12.3.6 data in resultSet is object when setting outFormat OBJECT + 12.3.7 the size of retrieved set can be set to 1 + 12.3.8 query 0 row + 12.3.9 Negative - To omit the first parameter + 12.3.10 Negative - set the 1st parameter of getRows() to be 0 + 12.3.11 Negative - set the 1st parameter of getRows() to be -5 + 12.3.12 Negative - set the 1st parameter of getRows() to be null + 12.4 Testing function getRow() + 12.4.1 works well with all correct setting + 12.4.2 data in resultSet is array when setting outFormat ARRAY + 12.4.3 data in resultSet is object when setting outFormat OBJECT + 12.4.4 query 0 row + 12.4.5 Negative - set the first parameter like getRows() + 12.5 Testing function close() + 12.5.1 does not call close() + 12.5.2 invokes close() twice + 12.5.3 uses getRows after calling close() + 12.5.4 closes one resultSet and then open another resultSet + 12.6 Testing metaData + 12.6.1 the amount and value of metaData should be correct + 12.6.2 can distinguish lower case and upper case + 12.6.3 can contain quotes + 12.6.4 can contain underscore + 12.7 Testing maxRows + 12.7.1 maxRows option is ignored when resultSet option is true + 12.7.2 maxRows option is ignored with REF Cursor + +21. datatypeAssist.js + +22. dataTypeChar.js + 22.1 supports CHAR data + 22.2 resultSet stores CHAR data correctly + 22.3 stores null value correctly + +23. dataTypeNchar.js + 23.1 supports NCHAR data type + 23.2 resultSet supports NCHAR data type + 23.3 stores null value correctly + +24. dataTypeVarchar2.js + 24.1 supports VARCHAR2 data in various lengths + 24.2 resultSet stores VARCHAR2 data correctly + 24.3 stores null value correctly + +25. dataTypeNvarchar2.js + 25.1 supports NVARCHAR2 data in various lengths + 25.2 resultSet stores NVARCHAR2 data correctly + 25.3 stores null value correctly + +26. dataTypeNumber.js + 26.1 supports NUMBER data type + 26.2 resultSet stores NUMBER data correctly + 26.3 stores null value correctly + +27. dataTypeNumber2.js + 27.1 supports NUMBER(p, s) data type + 27.2 resultSet stores NUMBER(p, s) data correctly + 27.3 stores null value correctly + +28. dataTypeFloat.js + 28.1 supports FLOAT data type + 28.2 resultSet stores FLOAT data correctly + 28.3 stores null value correctly + +29. dataTypeFloat2.js + 29.1 supports FLOAT(p) data type + 29.2 resultSet stores FLOAT(p) data correctly + 29.3 stores null value correctly + +30. dataTypeBinaryFloat.js + - supports BINARY_FLOAT data type + +31. dataTypeBinaryDouble.js + - supports BINARY_DOUBLE data type + +32. dataTypeDate.js + 32.1 supports DATE data type + 32.2 resultSet stores DATE data correctly + 32.3 stores null value correctly + +33. dataTypeTimestamp1.js + 33.1 supports TIMESTAMP data type + 33.2 resultSet stores TIMESTAMP data correctly + 33.3 stores null value correctly + +34. dataTypeTimestamp2.js + 34.1 supports TIMESTAMP(p) data type + 34.2 resultSet stores TIMESTAMP data correctly + 34.3 stores null value correctly + +35. dataTypeTimestamp3.js + supports TIMESTAMP WITH TIME ZONE data type + +36. dataTypeTimestamp4.js + supports TIMESTAMP WITH TIME ZONE data type + +37. dataTypeTimestamp5.js + 37.1 supports TIMESTAMP WITH LOCAL TIME ZONE data type + 37.2 resultSet stores TIMESTAMP WITH LOCAL TIME ZONE data correctly + 37.3 stores null value correctly + +38. dataTypeTimestamp6.js + 38.1 supports TIMESTAMP(9) WITH LOCAL TIME ZONE data type + 38.2 resultSet stores TIMESTAMP(9) WITH LOCAL TIME ZONE data correctly + 38.3 stores null value correctly + +39. dataTypeRowid.js + 39.1 supports ROWID data type + +40. dataTypeClob.js + 40.1 processes null value correctly + 40.2 stores CLOB value correctly + +41. dataTypeBlob + 41.1 processes null value correctly + 41.2 stores BLOB value correctly + +51. accessTerminatedPoolAttributes.js + can not access attributes of terminated pool + +52. getConnAfterPoolTerminate.js + can not get connections from pool after pool is terminated + +53. poolValidityAfterFailingTernimate.js + pool should be available after failing terminate + +54. releaseAfterFailingTerminate.js + can still release connections after failing pool termination + +55. resultSet2.js + 55.1 query a RDBMS function + 55.1.1 LPAD function + 55.2 binding variables + 55.2.1 query with one binding variable + 55.3 alternating getRow() & getRows() function + 55.3.1 result set + 55.3.2 REF Cursor + 55.4 release connection before close resultSet + 55.4.1 result set + 55.4.2 REF Cursor + 55.5 the content of resultSet should be consistent + 55.5.1 (1) get RS (2) modify data in that table and commit (3) check RS + 55.6 access resultSet simultaneously + 55.6.1 concurrent operations on resultSet are not allowed + 55.6.2 concurrent operation on REF Cursor are not allowed + 55.7 getting multiple resultSets + 55.7.1 can access multiple resultSet on one connection + 55.7.2 can access multiple REF Cursor + 55.8 Negative - resultSet is only for query statement + 55.8.1 resultSet cannot be returned for non-query statements + 55.9 test querying a PL/SQL function + 55.9.1 + 55.10 calls getRows() once and then close RS before getting more rows + 55.10.1 + +56. fetchAs.js + 56.1 FetchAs - DATE type as STRING + 56.2 FetchAs NUMBER & DATE type as STRING + 56.3 FetchAs Oracledb property by-type + 56.4 FetchAs override oracledb by-type (for DATE) at execute time + + 57. nestedCursor.js + 57.1 testing nested cursor support - result set + 57.2 testing nested cursor support - REF Cursor \ No newline at end of file diff --git a/test/nestedCursor.js b/test/nestedCursor.js new file mode 100644 index 00000000..e7c4b3dd --- /dev/null +++ b/test/nestedCursor.js @@ -0,0 +1,303 @@ +/* Copyright (c) 2015, 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 + * 57. nestedCursor.js + * + * DESCRIPTION + * Testing nested cursor. + * + * 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('57. nestedCursor.js', function() { + + if(dbConfig.externalAuth){ + var credential = { externalAuth: true, connectString: dbConfig.connectString }; + } else { + var credential = dbConfig; + } + + var createParentTable = + "BEGIN \ + DECLARE \ + e_table_exists EXCEPTION; \ + PRAGMA EXCEPTION_INIT(e_table_exists, -00942); \ + BEGIN \ + EXECUTE IMMEDIATE ('DROP TABLE test_parent_tab'); \ + EXCEPTION \ + WHEN e_table_exists \ + THEN NULL; \ + END; \ + EXECUTE IMMEDIATE (' \ + CREATE TABLE test_parent_tab ( \ + id NUMBER, \ + description VARCHAR2(32), \ + CONSTRAINT parent_tab_pk PRIMARY KEY (id) \ + ) \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO test_parent_tab (id, description) \ + VALUES \ + (1,''Parent 1'') \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO test_parent_tab (id, description) \ + VALUES \ + (2,''Parent 2'') \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO test_parent_tab (id, description) \ + VALUES \ + (3,''Parent 3'') \ + '); \ + END; "; + + var createChildTable = + "BEGIN \ + DECLARE \ + e_table_exists EXCEPTION; \ + PRAGMA EXCEPTION_INIT(e_table_exists, -00942); \ + BEGIN \ + EXECUTE IMMEDIATE ('DROP TABLE test_child_tab'); \ + EXCEPTION \ + WHEN e_table_exists \ + THEN NULL; \ + END; \ + EXECUTE IMMEDIATE (' \ + CREATE TABLE test_child_tab ( \ + id NUMBER, \ + parent_id NUMBER, \ + description VARCHAR2(32), \ + CONSTRAINT child_tab_pk PRIMARY KEY (id), \ + CONSTRAINT child_parent_fk FOREIGN KEY (parent_id) REFERENCES test_parent_tab(id) \ + ) \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO test_child_tab (id, parent_id, description) \ + VALUES \ + (1, 1, ''Child 1'') \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO test_child_tab (id, parent_id, description) \ + VALUES \ + (2, 1, ''Child 2'') \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO test_child_tab (id, parent_id, description) \ + VALUES \ + (3, 2, ''Child 3'') \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO test_child_tab (id, parent_id, description) \ + VALUES \ + (4, 2, ''Child 4'') \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO test_child_tab (id, parent_id, description) \ + VALUES \ + (5, 2, ''Child 5'') \ + '); \ + EXECUTE IMMEDIATE (' \ + INSERT INTO test_child_tab (id, parent_id, description) \ + VALUES \ + (6, 3, ''Child 6'') \ + '); \ + END; "; + + var cursorExpr = + "CREATE OR REPLACE PROCEDURE cursor_parent_child (p_out OUT SYS_REFCURSOR) \ + AS \ + BEGIN \ + OPEN p_out FOR \ + SELECT p "; + + var connection = false; + before(function(done) { + async.series([ + function(callback) { + oracledb.getConnection( + credential, + function(err, conn) { + connection = conn; + callback(); + } + ); + }, + function(callback) { + connection.should.be.ok; + connection.execute( + createParentTable, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.should.be.ok; + connection.execute( + createChildTable, + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + + }) + + after(function(done) { + async.series([ + function(callback) { + connection.execute( + "DROP TABLE test_child_tab", + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "DROP TABLE test_parent_tab", + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.release( function(err) { + should.not.exist(err); + callback(); + }); + } + ], done); + }) + + function fetchOneRowFromRS(rs, cb) { + rs.getRow(function(err, row) { + if(err) { + // console.error("Error at accessing RS: " + err.message); + // NJS-010: unsupported data type in select list + (err.message).should.startWith('NJS-010'); + rs.close(function(err) { + should.not.exist(err); + cb(); + }); + } else if(row) { + console.log(row); + fetchOneRowFromRS(rs, cb); + } else { + rs.close(function(err) { + should.not.exist(err); + cb(); + }); + } + }); + } + + it('57.1 testing nested cursor support - result set', function(done) { + connection.should.be.ok; + + var sql = + "SELECT p.description, \ + CURSOR( \ + SELECT c.description \ + FROM test_child_tab c \ + WHERE c.parent_id = p.id \ + ) children \ + FROM test_parent_tab p"; + + connection.execute( + sql, + [], + { resultSet: true }, + function(err, result) { + should.not.exist(err); + should.exist(result.resultSet); + fetchOneRowFromRS(result.resultSet, done); + } + ); + + }) + + it('57.2 testing nested cursor support - REF Cursor', function(done) { + var testproc = + "CREATE OR REPLACE PROCEDURE get_family_tree(p_out OUT SYS_REFCURSOR) \ + AS \ + BEGIN \ + OPEN p_out FOR \ + SELECT p.description, \ + CURSOR( \ + SELECT c.description \ + FROM test_child_tab c \ + WHERE c.parent_id = p.id \ + ) children \ + FROM test_parent_tab p; \ + END; "; + + async.series([ + function(callback) { + connection.execute( + testproc, + function(err, result) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback){ + connection.execute( + "BEGIN get_family_tree(:out); END;", + { + out: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } + }, + function(err, result) { + should.not.exist(err); + fetchOneRowFromRS(result.outBinds.out, callback); + } + ); + }, + function(callback) { + connection.execute( + "DROP PROCEDURE get_family_tree", + function(err, result) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + }) +}) diff --git a/test/resultSet1.js b/test/resultSet1.js index 89863379..647ee2fe 100644 --- a/test/resultSet1.js +++ b/test/resultSet1.js @@ -346,7 +346,7 @@ describe('12. resultSet1.js', function() { }) describe('12.3 Testing function getRows()', function() { - it('12.3.1 retrived set is exactly the size of result', function(done) { + it('12.3.1 retrieved set is exactly the size of result', function(done) { connection.should.be.ok; var nRows = rowsAmount; var accessCount = 0; @@ -379,7 +379,7 @@ describe('12. resultSet1.js', function() { } }) - it('12.3.2 retrived set is greater than the size of result', function(done) { + it('12.3.2 retrieved set is greater than the size of result', function(done) { connection.should.be.ok; var nRows = rowsAmount * 2; var accessCount = 0; @@ -412,7 +412,7 @@ describe('12. resultSet1.js', function() { } }) - it('12.3.3 retrived set is half of the size of result', function(done) { + it('12.3.3 retrieved set is half of the size of result', function(done) { connection.should.be.ok; var nRows = Math.ceil(rowsAmount/2); var accessCount = 0; @@ -445,7 +445,7 @@ describe('12. resultSet1.js', function() { } }) - it('12.3.4 retrived set is one tenth of the size of the result', function(done) { + it('12.3.4 retrieved set is one tenth of the size of the result', function(done) { connection.should.be.ok; var nRows = Math.ceil(rowsAmount/10); var accessCount = 0; @@ -550,7 +550,7 @@ describe('12. resultSet1.js', function() { } }) - it('12.3.7 the size of retrived set can be set to 1', function(done) { + it('12.3.7 the size of retrieved set can be set to 1', function(done) { connection.should.be.ok; var nRows = 1; var accessCount = 0; @@ -1318,6 +1318,71 @@ describe('12. resultSet1.js', function() { }); } }) + + it('12.7.2 maxRows option is ignored with REF Cursor', function(done) { + connection.should.be.ok; + var rowCount = 0; + var queryAmount = 100; + var proc = + "CREATE OR REPLACE PROCEDURE get_emp_rs (p_in IN NUMBER, p_out OUT SYS_REFCURSOR) \ + AS \ + BEGIN \ + OPEN p_out FOR \ + SELECT * FROM oracledb_employees \ + WHERE employees_id <= p_in; \ + END; "; + + async.series([ + function(callback) { + connection.execute( + proc, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "BEGIN get_emp_rs(:in, :out); END;", + { + in: queryAmount, + out: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } + }, + { maxRows: 10 }, + function(err, result) { + should.not.exist(err); + fetchRowFromRS(result.outBinds.out, callback); + } + ); + }, + function(callback) { + connection.execute( + "DROP PROCEDURE get_emp_rs", + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + + function fetchRowFromRS(rs, cb) { + rs.getRow(function(err, row) { + should.not.exist(err); + if(row) { + rowCount++; + return fetchRowFromRS(rs, cb); + } else { + rs.close( function(err) { + should.not.exist(err); + rowCount.should.eql(queryAmount); + cb(); + }); + } + }); + } + }) }) }) diff --git a/test/resultSet2.js b/test/resultSet2.js index 3ab390f0..c81dfd70 100644 --- a/test/resultSet2.js +++ b/test/resultSet2.js @@ -31,13 +31,14 @@ * 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('55 resultSet2.js', function() { +describe('55. resultSet2.js', function() { if(dbConfig.externalAuth){ var credential = { externalAuth: true, connectString: dbConfig.connectString }; @@ -65,6 +66,7 @@ describe('55 resultSet2.js', function() { '); \ END; "; + var rowsAmount = 300; var insertRows = "DECLARE \ x NUMBER := 0; \ @@ -76,37 +78,89 @@ describe('55 resultSet2.js', function() { INSERT INTO oracledb_employees VALUES (x, n); \ END LOOP; \ END; "; - var rowsAmount = 300; - + + var proc = + "CREATE OR REPLACE PROCEDURE get_emp_rs (p_in IN NUMBER, p_out OUT SYS_REFCURSOR) \ + AS \ + BEGIN \ + OPEN p_out FOR \ + SELECT * FROM oracledb_employees \ + WHERE employees_id > p_in; \ + END; "; + beforeEach(function(done) { - oracledb.getConnection(credential, function(err, conn) { - if(err) { console.error(err.message); return; } - connection = conn; - connection.execute(createTable, function(err) { - if(err) { console.error(err.message); return; } + async.series([ + function(callback) { + oracledb.getConnection( + credential, + function(err, conn) { + connection = conn; + callback(); + } + ); + }, + function(callback) { + connection.should.be.ok; connection.execute( - insertRows, + createTable, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + insertRows, [], { autoCommit: true }, function(err) { - if(err) { console.error(err.message); return; } - done(); - }); - }); - }); + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + proc, + [], + { autoCommit: true }, + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], done); }) afterEach(function(done) { - connection.execute( - 'DROP TABLE oracledb_employees', - function(err) { - if(err) { console.error(err.message); return; } - connection.release(function(err) { - if(err) { console.error(err.message); return; } - done(); + async.series([ + function(callback) { + connection.execute( + 'DROP TABLE oracledb_employees', + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + 'DROP PROCEDURE get_emp_rs', + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.release( function(err) { + should.not.exist(err); + callback(); }); } - ); + ], done); }) describe('55.1 query a RDBMS function', function() { @@ -174,8 +228,8 @@ describe('55 resultSet2.js', function() { }) - describe('55.3 alternating getRow() & getRows() function', function(done) { - it('55.3.1', function(done) { + describe('55.3 alternating getRow() & getRows() function', function() { + it('55.3.1 result set', function(done) { connection.should.be.ok; var accessCount = 0; var numRows = 4; @@ -229,11 +283,85 @@ describe('55 resultSet2.js', function() { } } }) + + it('55.3.2 REF Cursor', function(done) { + connection.should.be.ok; + var accessCount = 0; + var numRows = 4; + var flag = 1; // 1 - getRow(); 2 - getRows(); 3 - to close resultSet. + + connection.execute( + "BEGIN get_emp_rs(:in, :out); END;", + { + in: 200, + out: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } + }, + function(err, result) { + should.not.exist(err); + fetchRowFromRS(result.outBinds.out, done); + } + ); + + function fetchRowFromRS(rs, cb) { + if(flag === 1) { + rs.getRow(function(err, row) { + should.not.exist(err); + if(row) { + flag = 2; + accessCount++; + return fetchRowFromRS(rs, cb); + } else { + flag = 3; + return fetchRowFromRS(rs, cb); + } + }); + } + else if(flag === 2) { + rs.getRows(numRows, function(err, rows) { + should.not.exist(err); + if(rows.length > 0) { + flag = 1; + accessCount++; + return fetchRowFromRS(rs, cb); + } else { + flag = 3; + return fetchRowFromRS(rs, cb); + } + }); + } + else if(flag === 3) { + // console.log("resultSet is empty!"); + rs.close(function(err) { + should.not.exist(err); + // console.log("Total access count is " + accessCount); + accessCount.should.be.exactly((100/(numRows + 1)) * 2); + cb(); + }); + } + } + }) }) - describe('55.4 release connetion before close resultSet', function() { + describe('55.4 release connection before close resultSet', function() { var conn2 = false; - before(function(done) { + function fetchRowFromRS(rs, cb) { + rs.getRow(function(err, row) { + if(row) { + return fetchRowFromRS(rs, cb); + } else { + conn2.release(function(err) { + should.not.exist(err); + rs.close(function(err) { + should.exist(err); + err.message.should.startWith('NJS-003'); // invalid connection + cb(); + }); + }); + } + }); + } + + beforeEach(function(done) { oracledb.getConnection( credential, function(err, conn) { @@ -244,7 +372,7 @@ describe('55 resultSet2.js', function() { ); }) - it('55.4.1 ', function(done) { + it('55.4.1 result set', function(done) { conn2.should.be.ok; conn2.execute( "SELECT * FROM oracledb_employees", @@ -252,28 +380,25 @@ describe('55 resultSet2.js', function() { { resultSet: true }, function(err, result) { should.not.exist(err); - fetchRowFromRS(result.resultSet); + fetchRowFromRS(result.resultSet, done); } ); + }) + + it('55.4.2 REF Cursor', function(done) { + conn2.should.be.ok; - function fetchRowFromRS(rs) { - rs.getRow(function(err, row) { + conn2.execute( + "BEGIN get_emp_rs(:in, :out); END;", + { + in: 200, + out: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } + }, + function(err, result) { should.not.exist(err); - if(row) { - return fetchRowFromRS(rs); - } else { - conn2.release(function(err) { - should.not.exist(err); - rs.close(function(err) { - should.exist(err); - err.message.should.startWith('NJS-003'); - done(); - }); - }); - } - - }); - } + fetchRowFromRS(result.outBinds.out, done); + } + ); }) }) @@ -312,13 +437,13 @@ describe('55 resultSet2.js', function() { ], done); function fetchRowFromRS(rset, cb) { - rs.getRow(function(err, row) { + rset.getRow(function(err, row) { should.not.exist(err); if(row) { rowsCount++; return fetchRowFromRS(rset, cb); } else { - rs.close(function(err) { + rset.close(function(err) { should.not.exist(err); rowsCount.should.eql(rowsAmount); cb(); @@ -328,14 +453,45 @@ describe('55 resultSet2.js', function() { } }) + }) describe('55.6 access resultSet simultaneously', function() { + var numRows = 10; // number of rows to return from each call to getRows() + + function fetchRowFromRS(rs, cb) { + rs.getRow(function(err, row) { + if(err) { + cb(err); + return; + } else { + if(row) { + return fetchRowFromRS(rs, cb); + } else { + cb(); + } + } + }); + } + + function fetchRowsFromRS(rs, cb) { + rs.getRows(numRows, function(err, rows) { + if(err) { + cb(err); + return; + } else { + if(rows.length > 0) { + return fetchRowsFromRS(rs, cb); + } else { + cb(); + } + } + }); + } + it('55.6.1 concurrent operations on resultSet are not allowed', function(done) { connection.should.be.ok; - var rowCount1 = 0; - var rowCount2 = 0; - var numRows = 10; // number of rows to return from each call to getRows() + connection.execute( "SELECT * FROM oracledb_employees", [], @@ -364,49 +520,80 @@ describe('55 resultSet2.js', function() { } }); } - ); - - function fetchRowFromRS(rs, cb) { - rs.getRow(function(err, row) { - if(err) { - cb(err); - return; - } else { - if(row) { - rowCount1++; - return fetchRowFromRS(rs, cb); - } else { - cb(); - } - } - }); - } - - function fetchRowsFromRS(rs, cb) { - rs.getRows(numRows, function(err, rows) { - //should.not.exist(err); - if(err) { - cb(err); - return; - } else { - if(rows.length > 0) { - rowCount2 += 10; - return fetchRowsFromRS(rs, cb); - } else { - cb(); - } - } - }); - } + ); }) + + it('55.6.2 concurrent operation on REF Cursor are not allowed', function(done) { + connection.should.be.ok; + + connection.execute( + "BEGIN get_emp_rs(:in, :out); END;", + { + in: 0, + out: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } + }, + function(err, result) { + should.not.exist(err); + async.parallel([ + function(callback) { + fetchRowFromRS(result.outBinds.out, callback); + }, + function(callback) { + fetchRowsFromRS(result.outBinds.out, callback); + } + ], function(err) { + if(err) { + // console.log(err); + err.message.should.startWith('NJS-017'); + result.outBinds.out.close(function(err) { + done(); + }); + } else { + result.outBinds.out.close(function(error) { + should.not.exist(error); + done(); + }); + } + }); + } + ); + }) + }) describe('55.7 getting multiple resultSets', function() { + var numRows = 10; // number of rows to return from each call to getRows() + + function fetchRowFromRS(rs, cb) { + rs.getRow(function(err, row) { + should.not.exist(err); + if(row) { + return fetchRowFromRS(rs, cb); + } else { + rs.close(function(err) { + should.not.exist(err); + cb(); + }); + } + }); + } + + function fetchRowsFromRS(rs, cb) { + rs.getRows(numRows, function(err, rows) { + should.not.exist(err); + if(rows.length > 0) { + return fetchRowsFromRS(rs, cb); + } else { + rs.close(function(err) { + should.not.exist(err); + cb(); + }); + } + }); + } + it('55.7.1 can access multiple resultSet on one connection', function(done) { connection.should.be.ok; - var rowCount1 = 0; - var rowCount2 = 0; - var numRows = 10; // number of rows to return from each call to getRows() async.parallel([ function(callback) { connection.execute( @@ -434,37 +621,42 @@ describe('55 resultSet2.js', function() { should.not.exist(err); done(); }); + }) + + it('55.7.2 can access multiple REF Cursor', function(done) { + connection.should.be.ok; - function fetchRowFromRS(rs, cb) { - rs.getRow(function(err, row) { - should.not.exist(err); - if(row) { - rowCount1++; - return fetchRowFromRS(rs, cb); - } else { - rs.close(function(err) { + async.parallel([ + function(callback) { + connection.execute( + "BEGIN get_emp_rs(:in, :out); END;", + { + in: 200, + out: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } + }, + function(err, result) { should.not.exist(err); - cb(); - }); - } - }); - } - - function fetchRowsFromRS(rs, cb) { - rs.getRows(numRows, function(err, rows) { - should.not.exist(err); - if(rows.length > 0) { - rowCount2 += numRows; - return fetchRowsFromRS(rs, cb); - } else { - rs.close(function(err) { + fetchRowFromRS(result.outBinds.out, callback); + } + ); + }, + function(callback) { + connection.execute( + "BEGIN get_emp_rs(:in, :out); END;", + { + in: 100, + out: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } + }, + function(err, result) { should.not.exist(err); - cb(); - }); - } - - }); - } + fetchRowsFromRS(result.outBinds.out, callback); + } + ); + } + ], function(err) { + should.not.exist(err); + done(); + }); }) }) @@ -486,23 +678,155 @@ describe('55 resultSet2.js', function() { }) }) + describe('55.9 test querying a PL/SQL function', function() { + it('55.9.1 ', function(done) { + var proc = + "CREATE OR REPLACE FUNCTION testfunc RETURN VARCHAR2 \ + IS \ + emp_name VARCHAR2(20); \ + BEGIN \ + SELECT 'Clark Kent' INTO emp_name FROM dual; \ + RETURN emp_name; \ + END; "; + + async.series([ + function(callback) { + connection.execute( + proc, + function(err) { + should.not.exist(err); + callback(); + } + ); + }, + function(callback) { + connection.execute( + "SELECT testfunc FROM dual", + [], + { resultSet: true }, + function(err, result) { + should.not.exist(err); + (result.resultSet.metaData[0].name).should.eql('TESTFUNC'); + fetchRowFromRS(result.resultSet, callback); + } + ); + }, + function(callback) { + connection.execute( + "DROP FUNCTION testfunc", + function(err, result) { + should.not.exist(err); + callback(); + } + ); + } + ], done); + + function fetchRowFromRS(rs, cb) { + rs.getRow(function(err, row) { + should.not.exist(err); + if(row) { + row[0].should.eql('Clark Kent'); + return fetchRowFromRS(rs, cb); + } else { + rs.close(function(err) { + should.not.exist(err); + cb(); + }); + } + }); + } + }) + }) + + describe('55.10 calls getRows() once and then close RS before getting more rows', function() { + it('55.10.1 ', function(done) { + connection.should.be.ok; + var numRows = 10; + var closeRS = true; + connection.execute( + "SELECT * FROM oracledb_employees", + [], + { resultSet: true }, + function(err, result) { + should.not.exist(err); + result.resultSet.getRows( + numRows, + function(err, rows) { + should.not.exist(err); + result.resultSet.close(function(err) { + should.not.exist(err); + fetchRowsFromRS(result.resultSet, numRows, done); + }); + } + ); + } + ); + + function fetchRowsFromRS(rs, numRows, done) { + rs.getRows(numRows, function(err, rows) { + should.exist(err); + err.message.should.startWith('NJS-018:'); // invalid result set + done(); + }); + } + }) + }) + + describe('55.11 deals with unsupported database with result set', function() { + var sql1 = "select dummy, HEXTORAW('0123456789ABCDEF0123456789ABCDEF') from dual"; + var sql2 = "SELECT dummy, rowid FROM dual"; + + function fetchOneRowFromRS(rs, cb) { + rs.getRow(function(err, row) { + /* Currently, even if the driver doesn't support certain data type + * the result set can still be created. + */ + // Error at accessing RS + if(err) { + // console.error("Error at accessing RS: " + err.message); + // NJS-010: unsupported data type in select list + (err.message).should.startWith('NJS-010'); + rs.close( function(err) { + should.not.exist(err); + cb(); + }); + } else if(row) { + console.log(row); + fetchOneRowFromRS(rs, cb); + } else { + rs.close( function(err) { + should.not.exist(err); + cb(); + }); + } + }); + } + + it('55.11.1 RAW data type', function(done) { + connection.should.be.ok; + connection.execute( + sql1, + [], + { resultSet: true }, + function(err, result) { + should.not.exist(err); + fetchOneRowFromRS(result.resultSet, done); + } + ); + }) + + it('55.11.2 ROWID date type', function(done) { + connection.execute( + sql2, + [], + { resultSet: true }, + function(err, result) { + should.not.exist(err); + fetchOneRowFromRS(result.resultSet, done); + } + ); + }) + }) + }) - - - - - - - - - - - - - - - - - - -