Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Support for INOUT types in stored procedures. Split tests into two fi…

…les. Minor refactoring
  • Loading branch information...
commit f9145b53ce0b53d02124e9f0249d87eb6ec35bba 1 parent 028743f
Damian Beresford authored
View
35 README.md
@@ -68,8 +68,43 @@ connection.execute("call myProc(:1,:2)", ["nodejs", new oracle.OutParam(oracle.O
```
+When using Strings as Out Params, the size can be optionally specified as follows:
+
+```
+
+connection.execute("call myProc(:1,:2)", ["nodejs", new oracle.OutParam(oracle.OCCISTRING, {size: 1000})], function(err, results){
+
+```
+
+If no size is specified, a default size of 200 chars is used.
+
See tests for more examples.
+## In/Out Params
+
+The following INOUT param types are supported:
+
+```
+
+OCCIINT
+OCCISTRING
+OCCIDOUBLE
+OCCIFLOAT
+OCCINUMBER
+
+```
+
+INOUT params are used like normal OUT prams, with the optional 'in' paramater value being passed in the options object:
+
+```
+
+connection.execute("call myProc(:1)", [new oracle.OutParam(oracle.OCCIINT, {in: 42})], function(err, results){
+ console.dir(results);
+};
+
+```
+
+
# Develop
## Install Oracle/Oracle Express
View
2  package.json
@@ -26,7 +26,7 @@
"nodeunit": "~>0.8.0"
},
"scripts": {
- "test": "nodeunit test"
+ "test": "nodeunit test/integration.js test/outparams.js"
},
"main": "./index.js"
}
View
40 src/connection.cpp
@@ -149,7 +149,6 @@ void Connection::closeConnection() {
}
}
-
void RandomBytesFree(char* data, void* hint) {
delete[] data;
}
@@ -174,20 +173,36 @@ int Connection::SetValuesOnStatement(oracle::occi::Statement* stmt, std::vector<
case VALUE_TYPE_DATE:
stmt->setDate(index, *((oracle::occi::Date*)val->value));
break;
- case VALUE_TYPE_OUTPUT:
+ case VALUE_TYPE_OUTPUT:
outParamType = ((OutParam*)val->value)->type();
switch(outParamType) {
case OutParam::OCCIINT:
- stmt->registerOutParam(index, oracle::occi::OCCIINT);
+ if (((OutParam*)val->value)->_inOut.hasInParam) {
+ stmt->setInt(index, ((OutParam*)val->value)->_inOut.intVal);
+ }else {
+ stmt->registerOutParam(index, oracle::occi::OCCIINT);
+ }
break;
case OutParam::OCCISTRING:
- stmt->registerOutParam(index, oracle::occi::OCCISTRING, ((OutParam*)val->value)->size());
+ if (((OutParam*)val->value)->_inOut.hasInParam) {
+ stmt->setString(index, ((OutParam*)val->value)->_inOut.stringVal);
+ }else {
+ stmt->registerOutParam(index, oracle::occi::OCCISTRING, ((OutParam*)val->value)->size());
+ }
break;
case OutParam::OCCIDOUBLE:
- stmt->registerOutParam(index, oracle::occi::OCCIDOUBLE);
+ if (((OutParam*)val->value)->_inOut.hasInParam) {
+ stmt->setDouble(index, ((OutParam*)val->value)->_inOut.doubleVal);
+ }else {
+ stmt->registerOutParam(index, oracle::occi::OCCIDOUBLE);
+ }
break;
case OutParam::OCCIFLOAT:
- stmt->registerOutParam(index, oracle::occi::OCCIFLOAT);
+ if (((OutParam*)val->value)->_inOut.hasInParam) {
+ stmt->setFloat(index, ((OutParam*)val->value)->_inOut.floatVal);
+ }else {
+ stmt->registerOutParam(index, oracle::occi::OCCIFLOAT);
+ }
break;
case OutParam::OCCICURSOR:
stmt->registerOutParam(index, oracle::occi::OCCICURSOR);
@@ -202,8 +217,14 @@ int Connection::SetValuesOnStatement(oracle::occi::Statement* stmt, std::vector<
stmt->registerOutParam(index, oracle::occi::OCCITIMESTAMP);
break;
case OutParam::OCCINUMBER:
- stmt->registerOutParam(index, oracle::occi::OCCINUMBER);
+ {
+ if (((OutParam*)val->value)->_inOut.hasInParam) {
+ stmt->setNumber(index, ((OutParam*)val->value)->_inOut.numberVal);
+ } else {
+ stmt->registerOutParam(index, oracle::occi::OCCINUMBER);
+ }
break;
+ }
case OutParam::OCCIBLOB:
stmt->registerOutParam(index, oracle::occi::OCCIBLOB);
break;
@@ -363,8 +384,7 @@ void Connection::EIO_Execute(uv_work_t* req) {
output->intVal = stmt->getInt(output->index);
break;
case OutParam::OCCISTRING:
- output->strVal = (const char*) new string;
- output->strVal = stmt->getString(output->index).c_str();
+ output->strVal = stmt->getString(output->index);
break;
case OutParam::OCCIDOUBLE:
output->doubleVal = stmt->getDouble(output->index);
@@ -608,7 +628,7 @@ void Connection::EIO_AfterExecute(uv_work_t* req, int status) {
obj->Set(String::New(returnParam.c_str()), Integer::New(output->intVal));
break;
case OutParam::OCCISTRING:
- obj->Set(String::New(returnParam.c_str()), String::New(output->strVal));
+ obj->Set(String::New(returnParam.c_str()), String::New(output->strVal.c_str()));
break;
case OutParam::OCCIDOUBLE:
obj->Set(String::New(returnParam.c_str()), Number::New(output->doubleVal));
View
2  src/executeBaton.h
@@ -44,7 +44,7 @@ struct value_t {
struct output_t {
int type;
int index;
- const char* strVal;
+ std::string strVal;
int intVal;
double doubleVal;
float floatVal;
View
45 src/outParam.cpp
@@ -1,5 +1,8 @@
#include "outParam.h"
+#include "nodeOracleException.h"
+#include <iostream>
+using namespace std;
Persistent<FunctionTemplate> OutParam::constructorTemplate;
@@ -17,15 +20,49 @@ void OutParam::Init(Handle<Object> target) {
Handle<Value> OutParam::New(const Arguments& args) {
HandleScope scope;
+ OutParam *outParam = new OutParam();
- OutParam *client = new OutParam();
- client->_type = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
- client->_size = args[1]->IsUndefined() ? 200 : args[1]->NumberValue();
- client->Wrap(args.This());
+ outParam->_type = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
+
+ if (!args[1]->IsUndefined()) {
+ REQ_OBJECT_ARG(1, opts);
+ OBJ_GET_NUMBER(opts, "size", outParam->_size, 200);
+
+ // check if there's an 'in' param
+ if (opts->Has(String::New("in"))) {
+ outParam->_inOut.hasInParam = true;
+ switch(outParam->_type) {
+ case OutParam::OCCIINT: {
+ OBJ_GET_NUMBER(opts, "in", outParam->_inOut.intVal, 0);
+ break;
+ }
+ case OutParam::OCCIDOUBLE: {
+ OBJ_GET_NUMBER(opts, "in", outParam->_inOut.doubleVal, 0);
+ break;
+ }
+ case OutParam::OCCIFLOAT: {
+ OBJ_GET_NUMBER(opts, "in", outParam->_inOut.floatVal, 0);
+ break;
+ }
+ case OutParam::OCCINUMBER: {
+ OBJ_GET_NUMBER(opts, "in", outParam->_inOut.numberVal, 0);
+ break;
+ }
+ case OutParam::OCCISTRING: {
+ OBJ_GET_STRING(opts, "in", outParam->_inOut.stringVal);
+ break;
+ }
+ default:
+ throw NodeOracleException("Unhandled OutPram type!");
+ }
+ }
+ }
+ outParam->Wrap(args.This());
return args.This();
}
OutParam::OutParam() {
+ _inOut.hasInParam = false;
}
OutParam::~OutParam() {
View
13 src/outParam.h
@@ -8,10 +8,22 @@
#include <unistd.h>
#endif
#include "utils.h"
+#include <occi.h>
using namespace node;
using namespace v8;
+struct inout_t {
+ bool hasInParam;
+ const char* stringVal;
+ int intVal;
+ double doubleVal;
+ float floatVal;
+ oracle::occi::Date dateVal;
+ oracle::occi::Timestamp timestampVal;
+ oracle::occi::Number numberVal;
+};
+
class OutParam : ObjectWrap {
public:
static void Init(Handle<Object> target);
@@ -20,6 +32,7 @@ class OutParam : ObjectWrap {
static v8::Handle<v8::Value> GetType(const v8::Arguments& args);
int _type;
int _size;
+ inout_t _inOut;
OutParam();
~OutParam();
View
178 test/integration.js
@@ -33,81 +33,6 @@
SELECT datatype_test_seq.nextval INTO :new.id FROM dual;
END;
/
- CREATE OR REPLACE PROCEDURE procNumericOutParam(param1 IN VARCHAR2, outParam1 OUT NUMBER)
- IS
- BEGIN
- DBMS_OUTPUT.PUT_LINE('Hello '|| param1);
- outParam1 := 42;
- END;
- /
- CREATE OR REPLACE PROCEDURE procStringOutParam(param1 IN VARCHAR2, outParam1 OUT STRING)
- IS
- BEGIN
- DBMS_OUTPUT.PUT_LINE('Hello '|| param1);
- outParam1 := 'Hello ' || param1;
- END;
- /
- CREATE OR REPLACE PROCEDURE procVarChar2OutParam(param1 IN VARCHAR2, outParam1 OUT VARCHAR2)
- IS
- BEGIN
- DBMS_OUTPUT.PUT_LINE('Hello '|| param1);
- outParam1 := 'Hello ' || param1;
- END;
- /
- CREATE OR REPLACE PROCEDURE procDoubleOutParam(param1 IN VARCHAR2, outParam1 OUT DOUBLE PRECISION)
- IS
- BEGIN
- outParam1 := -43.123456789012;
- END;
- /
- CREATE OR REPLACE PROCEDURE procFloatOutParam(param1 IN VARCHAR2, outParam1 OUT FLOAT)
- IS
- BEGIN
- outParam1 := 43;
- END;
- /
-
- CREATE OR REPLACE PROCEDURE procTwoOutParams(param1 IN VARCHAR2, outParam1 OUT NUMBER, outParam2 OUT STRING)
- IS
- BEGIN
- outParam1 := 42;
- outParam2 := 'Hello ' || param1;
- END;
- /
- CREATE OR REPLACE PROCEDURE procCursorOutParam(outParam OUT SYS_REFCURSOR)
- IS
- BEGIN
- open outParam for
- select * from person;
- END;
- /
- CREATE OR REPLACE PROCEDURE procCLOBOutParam(outParam OUT CLOB)
- IS
- BEGIN
- outParam := 'IAMCLOB';
- END;
- /
-
- BEGIN
- EXECUTE IMMEDIATE 'DROP TABLE basic_lob_table';
- EXCEPTION
- WHEN OTHERS THEN
- IF SQLCODE != -942 THEN
- RAISE;
- END IF;
- END;
- /
-
- create table basic_lob_table (x varchar2 (30), b blob, c clob);
- insert into basic_lob_table values('one', '010101010101010101010101010101', 'onetwothreefour');
- select * from basic_lob_table where x='one' and ROWNUM = 1;
-
- CREATE OR REPLACE PROCEDURE ReadBasicBLOB (outBlob OUT BLOB)
- IS
- BEGIN
- SELECT b INTO outBlob FROM basic_lob_table where x='one' and ROWNUM = 1;
- END;
- /
*/
var nodeunit = require("nodeunit");
@@ -211,107 +136,6 @@ exports['IntegrationTest'] = nodeunit.testCase({
});
},
- "stored procedures - numeric out param - occiint": function(test){
- var self = this;
- self.connection.execute("call procNumericOutParam(:1,:2)", ["node", new oracle.OutParam()], function(err, results){
- if(err) { console.error(err); return; }
- test.equal(results.returnParam, 42);
- test.done();
- });
- },
-
- "stored procedures - numeric out param - occinumber": function(test){
- var self = this;
- self.connection.execute("call procNumericOutParam(:1,:2)", ["node", new oracle.OutParam(oracle.OCCINUMBER)], function(err, results){
- if(err) { console.error(err); return; }
- test.equal(results.returnParam, 42);
- test.done();
- });
- },
-
- "stored procedures - string out param": function(test){
- var self = this;
- self.connection.execute("call procStringOutParam(:1,:2)", ["node", new oracle.OutParam(oracle.OCCISTRING)], function(err, results){
- if(err) { console.error(err); return; }
- test.equal(results.returnParam, "Hello node");
- test.done();
- });
- },
-
- "stored procedures - varchar2 out param": function(test){
- var self = this;
- self.connection.execute("call procVarChar2OutParam(:1,:2)", ["node", new oracle.OutParam(oracle.OCCISTRING, 40)], function(err, results){
- if(err) { console.error(err); return; }
- test.equal(results.returnParam, "Hello node");
- test.done();
- });
- },
-
- "stored procedures - double out param": function(test){
- var self = this;
- self.connection.execute("call procDoubleOutParam(:1,:2)", ["node", new oracle.OutParam(oracle.OCCIDOUBLE)], function(err, results){
- if(err) { console.error(err); return; }
- test.equal(results.returnParam, -43.123456789012);
- test.done();
- });
- },
-
- "stored procedures - float out param": function(test){
- var self = this;
- self.connection.execute("call procFloatOutParam(:1,:2)", ["node", new oracle.OutParam(oracle.OCCIFLOAT)], function(err, results){
- if(err) { console.error(err); return; }
- // purposely commented, gotta love floats in javasctipt: http://stackoverflow.com/questions/588004/is-javascripts-floating-point-math-broken
- // test.equal(results.returnParam, 43);
- test.done();
- });
- },
-
- "stored procedures - multiple out params": function(test){
- var self = this;
- self.connection.execute("call procTwoOutParams(:1,:2,:3)", ["node", new oracle.OutParam(oracle.OCCIINT), new oracle.OutParam(oracle.OCCISTRING)], function(err, results){
- if(err) { console.error(err); return; }
- test.equal(results.returnParam, 42);
- test.equal(results.returnParam1, "Hello node");
- test.done();
- });
- },
-
- "stored procedures - cursor out param": function(test){
- var self = this;
- self.connection.execute("call procCursorOutParam(:1)", [new oracle.OutParam(oracle.OCCICURSOR)], function(err, results){
- if(err) { console.error(err); return; }
- test.equal(results.returnParam.length, 0);
- test.done();
- });
- },
-
- "stored procedures - clob out param": function(test){
- var self = this;
- self.connection.execute("call procCLOBOutParam(:1)", [new oracle.OutParam(oracle.OCCICLOB)], function(err, results){
- if(err) { console.error(err); return; }
- test.equal(results.returnParam, "IAMCLOB");
- test.done();
- });
- },
-
- "stored procedures - date timestamp out param": function(test){
- var self = this;
- self.connection.execute("call procDateTimeOutParam(:1, :2)", [new oracle.OutParam(oracle.OCCIDATE), new oracle.OutParam(oracle.OCCITIMESTAMP)], function(err, results){
- if(err) { console.error(err); return; }
- var d = new Date();
- test.equal(results.returnParam.getFullYear(), d.getFullYear());
- test.done();
- });
- },
-
- "stored procedures - blob out param": function(test){
- var self = this;
- self.connection.execute("call ReadBasicBLOB(:1)", [new oracle.OutParam(oracle.OCCIBLOB)], function(err, results){
- if(err) { console.error(err); return; }
- test.done();
- });
- },
-
"datatypes null": function(test) {
var self = this;
self.connection.execute(
@@ -351,5 +175,5 @@ exports['IntegrationTest'] = nodeunit.testCase({
test.done();
});
});
- }
+ }
});
View
230 test/outparams.js
@@ -0,0 +1,230 @@
+
+/*
+ tests-settings.json:
+ {
+ "hostname": "localhost",
+ "user": "test",
+ "password": "test"
+ }
+
+ Database:
+ CREATE OR REPLACE PROCEDURE procNumericOutParam(param1 IN VARCHAR2, outParam1 OUT NUMBER)
+ IS
+ BEGIN
+ DBMS_OUTPUT.PUT_LINE('Hello '|| param1);
+ outParam1 := 42;
+ END;
+ /
+ CREATE OR REPLACE PROCEDURE procStringOutParam(param1 IN VARCHAR2, outParam1 OUT STRING)
+ IS
+ BEGIN
+ DBMS_OUTPUT.PUT_LINE('Hello '|| param1);
+ outParam1 := 'Hello ' || param1;
+ END;
+ /
+ CREATE OR REPLACE PROCEDURE procVarChar2OutParam(param1 IN VARCHAR2, outParam1 OUT VARCHAR2)
+ IS
+ BEGIN
+ DBMS_OUTPUT.PUT_LINE('Hello '|| param1);
+ outParam1 := 'Hello ' || param1;
+ END;
+ /
+ CREATE OR REPLACE PROCEDURE procDoubleOutParam(param1 IN VARCHAR2, outParam1 OUT DOUBLE PRECISION)
+ IS
+ BEGIN
+ outParam1 := -43.123456789012;
+ END;
+ /
+ CREATE OR REPLACE PROCEDURE procFloatOutParam(param1 IN VARCHAR2, outParam1 OUT FLOAT)
+ IS
+ BEGIN
+ outParam1 := 43;
+ END;
+ /
+
+ CREATE OR REPLACE PROCEDURE procTwoOutParams(param1 IN VARCHAR2, outParam1 OUT NUMBER, outParam2 OUT STRING)
+ IS
+ BEGIN
+ outParam1 := 42;
+ outParam2 := 'Hello ' || param1;
+ END;
+ /
+ CREATE OR REPLACE PROCEDURE procCursorOutParam(outParam OUT SYS_REFCURSOR)
+ IS
+ BEGIN
+ open outParam for
+ select * from person;
+ END;
+ /
+ CREATE OR REPLACE PROCEDURE procCLOBOutParam(outParam OUT CLOB)
+ IS
+ BEGIN
+ outParam := 'IAMCLOB';
+ END;
+ /
+
+ BEGIN
+ EXECUTE IMMEDIATE 'DROP TABLE basic_lob_table';
+ EXCEPTION
+ WHEN OTHERS THEN
+ IF SQLCODE != -942 THEN
+ RAISE;
+ END IF;
+ END;
+ /
+
+ create table basic_lob_table (x varchar2 (30), b blob, c clob);
+ insert into basic_lob_table values('one', '010101010101010101010101010101', 'onetwothreefour');
+ select * from basic_lob_table where x='one' and ROWNUM = 1;
+
+ CREATE OR REPLACE PROCEDURE ReadBasicBLOB (outBlob OUT BLOB)
+ IS
+ BEGIN
+ SELECT b INTO outBlob FROM basic_lob_table where x='one' and ROWNUM = 1;
+ END;
+ /
+
+ CREATE OR REPLACE procedure doSquareInteger(z IN OUT Integer)
+ is
+ begin
+ z := z * z;
+ end;
+ /
+*/
+
+var nodeunit = require("nodeunit");
+var oracle = require("../");
+
+var settings = JSON.parse(require('fs').readFileSync('./tests-settings.json','utf8'));
+
+exports['IntegrationTest'] = nodeunit.testCase({
+ setUp: function(callback) {
+ var self = this;
+ oracle.connect(settings, function(err, connection) {
+ if(err) { callback(err); return; }
+ self.connection = connection;
+ callback();
+ });
+ },
+
+ tearDown: function(callback) {
+ if(this.connection) {
+ this.connection.close();
+ }
+ callback();
+ },
+
+ "stored procedures - multiple out params": function(test){
+ var self = this;
+
+ self.connection.execute("call procTwoOutParams(:1,:2,:3)", ["node", new oracle.OutParam(oracle.OCCIINT), new oracle.OutParam(oracle.OCCISTRING)], function(err, results){
+ if(err) { console.error(err); return; }
+ test.equal(results.returnParam, 42);
+ test.equal(results.returnParam1, "Hello node");
+ test.done();
+ });
+ },
+
+ "stored procedures - numeric out param - occiint": function(test){
+ var self = this;
+ self.connection.execute("call procNumericOutParam(:1,:2)", ["node", new oracle.OutParam()], function(err, results){
+ if(err) { console.error(err); return; }
+ test.equal(results.returnParam, 42);
+ test.done();
+ });
+ },
+
+ "stored procedures - numeric out param - occinumber": function(test){
+ var self = this;
+ self.connection.execute("call procNumericOutParam(:1,:2)", ["node", new oracle.OutParam(oracle.OCCINUMBER)], function(err, results){
+ if(err) { console.error(err); return; }
+ test.equal(results.returnParam, 42);
+ test.done();
+ });
+ },
+
+ "stored procedures - string out param": function(test){
+ var self = this;
+ self.connection.execute("call procStringOutParam(:1,:2)", ["node", new oracle.OutParam(oracle.OCCISTRING)], function(err, results){
+ if(err) { console.error(err); return; }
+ test.equal(results.returnParam, "Hello node");
+ test.done();
+ });
+ },
+
+ "stored procedures - varchar2 out param": function(test){
+ var self = this;
+ self.connection.execute("call procVarChar2OutParam(:1,:2)", ["node", new oracle.OutParam(oracle.OCCISTRING, {size: 40})], function(err, results){
+ if(err) { console.error(err); return; }
+ test.equal(results.returnParam, "Hello node");
+ test.done();
+ });
+ },
+
+
+ "stored procedures - double out param": function(test){
+ var self = this;
+ self.connection.execute("call procDoubleOutParam(:1,:2)", ["node", new oracle.OutParam(oracle.OCCIDOUBLE)], function(err, results){
+ if(err) { console.error(err); return; }
+ test.equal(results.returnParam, -43.123456789012);
+ test.done();
+ });
+ },
+
+ "stored procedures - float out param": function(test){
+ var self = this;
+ self.connection.execute("call procFloatOutParam(:1,:2)", ["node", new oracle.OutParam(oracle.OCCIFLOAT)], function(err, results){
+ if(err) { console.error(err); return; }
+ // purposely commented, gotta love floats in javasctipt: http://stackoverflow.com/questions/588004/is-javascripts-floating-point-math-broken
+ // test.equal(results.returnParam, 43);
+
+ test.done();
+ });
+ },
+
+ "stored procedures - cursor out param": function(test){
+ var self = this;
+ self.connection.execute("call procCursorOutParam(:1)", [new oracle.OutParam(oracle.OCCICURSOR)], function(err, results){
+ if(err) { console.error(err); return; }
+ test.equal(results.returnParam.length, 0);
+ test.done();
+ });
+ },
+
+ "stored procedures - clob out param": function(test){
+ var self = this;
+ self.connection.execute("call procCLOBOutParam(:1)", [new oracle.OutParam(oracle.OCCICLOB)], function(err, results){
+ if(err) { console.error(err); return; }
+ test.equal(results.returnParam, "IAMCLOB");
+ test.done();
+ });
+ },
+
+ "stored procedures - date timestamp out param": function(test){
+ var self = this;
+ self.connection.execute("call procDateTimeOutParam(:1, :2)", [new oracle.OutParam(oracle.OCCIDATE), new oracle.OutParam(oracle.OCCITIMESTAMP)], function(err, results){
+ if(err) { console.error(err); return; }
+ var d = new Date();
+ test.equal(results.returnParam.getFullYear(), d.getFullYear());
+ test.done();
+ });
+ },
+
+ "stored procedures - blob out param": function(test){
+ var self = this;
+ self.connection.execute("call ReadBasicBLOB(:1)", [new oracle.OutParam(oracle.OCCIBLOB)], function(err, results){
+ if(err) { console.error(err); return; }
+ test.done();
+ });
+ },
+
+ "stored procedures - in out params": function(test){
+ var self = this;
+ self.connection.execute("call doSquareInteger(:1)", [new oracle.OutParam(oracle.OCCIINT, {in: 5})], function(err, results){
+ if(err) { console.error(err); return; }
+ test.equal(results.returnParam, 25);
+ test.done();
+ });
+ }
+
+});
Please sign in to comment.
Something went wrong with that request. Please try again.