diff --git a/test/README.md b/test/README.md index 7c7ac313d..f6cd2c350 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 ee2339c45..2008426c6 100644 --- a/test/autoCommit.js +++ b/test/autoCommit.js @@ -31,142 +31,249 @@ * 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; } + 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) { + pool.getConnection( function(err, conn) { + should.not.exist(err); 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(); - }); - }); + function(callback) { + connection.execute( + script, + function(err) { + should.not.exist(err); + callback(); + } + ); } - ], done); - - + ], done); }) - afterEach(function(done){ - oracledb.outFormat = oracledb.ARRAY; - oracledb.autoCommit = false; - + after('drop table, release connection, terminate pool', function(done) { async.series([ - function(callback){ + function(callback) { connection.execute( - 'DROP TABLE oracledb_departments', - function(err){ - if(err) { console.error(err.message); return; } + "DROP TABLE oracledb_departments", + function(err) { + should.not.exist(err); callback(); } ); }, - function(callback){ - connection.release( function(err){ - if(err) { console.error(err.message); return; } + 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( + 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'", - 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(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.2 auto commit takes effect for DML - update', function(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){ - connection.execute( + 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){ + function(err) { should.not.exist(err); callback(); } ); }, - function(callback){ - connection.execute( + 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); @@ -174,18 +281,125 @@ 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.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 87ae90f4d..49a17e63b 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(); - }); + 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_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(); + } + ); + }) + }) - - 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", + 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); - callback(); + done(); } ); - } - ], 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) { + 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); - 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); + 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(); + } + ); + } + ); + }) + }) - 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) \ - 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", + 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); - callback(); + done(); } ); - } - ], 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) { + 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); - // 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); + 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(); + } + ); + } + ); + }) + }) - 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); - }) + 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 000000000..55074fdcf --- /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 9edb3e262..b03765920 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 3a2b37174..e1252f97c 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('1.4.1 stmtCacheSize = 0, which disable statement caching', function(done) { + connection.should.be.ok; + 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('works well when statement cache disabled', function(done) { + it('1.4.2 works well when statement cache enabled (stmtCacheSize > 0) ', function(done) { connection.should.be.ok; - (oracledb.stmtCacheSize).should.be.exactly(0); + 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,61 +514,83 @@ describe('1. connection.js', function(){ EXECUTE IMMEDIATE (' \ INSERT INTO oracledb_employees \ VALUES \ - (1001,''Chris Jones'') \ - '); \ - EXECUTE IMMEDIATE (' \ - INSERT INTO oracledb_employees \ - VALUES \ - (1002,''Tom Kyte'') \ + (1001,''Tom Kyte'') \ '); \ EXECUTE IMMEDIATE (' \ INSERT INTO oracledb_employees \ VALUES \ - (2001, ''Karen Morton'') \ + (1002, ''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(); - } - ); - }); + var conn1 = false; + var conn2 = false; + beforeEach('get 2 connections and create the table', function(done) { + async.series([ + function(callback) { + oracledb.getConnection(credential, function(err, conn) { + should.not.exist(err); + conn1 = conn; + callback(); + }); + }, + function(callback) { + oracledb.getConnection(credential, function(err, conn) { + should.not.exist(err); + conn2 = conn; + callback(); + }); + }, + function(callback) { + conn1.should.be.ok; + conn1.execute( + makeTable, + [], + { autoCommit: true }, + function(err) { + should.not.exist(err); + callback(); + } + ); + } + ], 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(); + 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('works well when statement cache enabled', function(done) { - connection.should.be.ok; - (oracledb.stmtCacheSize).should.be.exactly(100); - + + it('1.5.1 commit() function works well', function(done) { async.series([ function(callback) { - connection.execute( + conn2.execute( "INSERT INTO oracledb_employees VALUES (:num, :str)", - { num: 1003, str: 'Robyn Sands' }, - { autoCommit: true }, + { num: 1003, str: 'Patrick Engebresson' }, function(err) { should.not.exist(err); callback(); @@ -524,27 +598,93 @@ describe('1. connection.js', function(){ ); }, function(callback) { - connection.execute( - "INSERT INTO oracledb_employees VALUES (:num, :str)", - { num: 1004, str: 'Bryant Lin' }, - { autoCommit: true }, - function(err) { + 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) { - connection.execute( + 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: 1005, str: 'Patrick Engebresson' }, - { autoCommit: true }, + { 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 000000000..e31d45a25 --- /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 000000000..be91c5fa7 --- /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 000000000..ba56665f0 --- /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 de16cefdb..2913a2c09 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 6c43f8972..47772011c 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 d378d2e87..0997e9cde 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 4f52f0d57..d8fa2a71f 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 b30cc0fbd..2a3184946 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 000000000..c47997abb --- /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 000000000..9b8907d07 Binary files /dev/null and b/test/fuzzydinosaur.jpg differ diff --git a/test/list.txt b/test/list.txt new file mode 100644 index 000000000..d5912d55c --- /dev/null +++ b/test/list.txt @@ -0,0 +1,339 @@ +1. connection.js + 1.1 can run SQL query with different output formats + 1.1.1 ARRAY format by default + 1.1.2 ARRAY format explicitly + 1.1.3 OBJECT format + 1.1.4 Negative test - invalid outFormat value + 1.2 limits the number of rows fetched + 1.2.1 by default, the number is 100 + 1.2.2 can also specify for each execution + 1.2.3 can set maxRows to be 0 + 1.2.4 cannot set maxRows to be a negative number + 1.2.5 sets maxRows to be very large value + 1.3 can call PL/SQL procedures + 1.3.1 bind parameters in various ways + 1.4 statementCacheSize controls statement caching + 1.4.1 stmtCacheSize = 0, which disable statement caching + 1.4.2 works well when statement cache enabled (stmtCacheSize > 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 000000000..e7c4b3dd7 --- /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 898633799..647ee2fe2 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 3ab390f04..c81dfd70a 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(); + ); + }) + + 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 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); + ], function(err) { + if(err) { + // console.log(err); + err.message.should.startWith('NJS-017'); + result.outBinds.out.close(function(err) { + done(); + }); } else { - cb(); - } - } - }); - } + 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,12 +621,112 @@ 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; + + 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); + 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); + fetchRowsFromRS(result.outBinds.out, callback); + } + ); + } + ], function(err) { + should.not.exist(err); + done(); + }); + }) + }) + + describe('55.8 Negative - resultSet is only for query statement', function() { + it('55.8.1 resultSet cannot be returned for non-query statements', function(done) { + connection.should.be.ok; + connection.execute( + "UPDATE oracledb_employees SET employees_name = 'Alan' WHERE employees_id = 100", + [], + { resultSet: true }, + function(err, result) { + should.exist(err); + // console.log(err); + err.message.should.startWith('NJS-019'); + done(); + } + ); + + }) + }) + + 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) { - rowCount1++; + row[0].should.eql('Clark Kent'); return fetchRowFromRS(rs, cb); } else { rs.close(function(err) { @@ -449,60 +736,97 @@ describe('55 resultSet2.js', function() { } }); } - - 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) { - should.not.exist(err); - cb(); - }); - } - - }); - } }) }) - describe('55.8 Negative - resultSet is only for query statement', function() { - it('55.8.1 resultSet cannot be returned for non-query statements', function(done) { + 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( - "UPDATE oracledb_employees SET employees_name = 'Alan' WHERE employees_id = 100", + "SELECT * FROM oracledb_employees", [], { resultSet: true }, function(err, result) { - should.exist(err); - // console.log(err); - err.message.should.startWith('NJS-019'); - done(); + 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); + } + ); + }) + }) +})