Skip to content

getColumnMetaData option #224

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 40 additions & 4 deletions src/connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,24 @@ uni::CallbackType Connection::Execute(const uni::FunctionCallbackInfo& args) {

REQ_STRING_ARG(0, sql);
REQ_ARRAY_ARG(1, values);
REQ_FUN_ARG(2, callback);
Local<Object> options;
int cbIndex = 2;
if (args.Length() > 2 && args[2]->IsObject() && !args[2]->IsFunction())
{
options = Local<Object>::Cast(args[2]);
++cbIndex;
}
if (args.Length() <= cbIndex || !args[cbIndex]->IsFunction())
{
ostringstream oss;
oss << "Argument " << cbIndex << " must be a function";
UNI_THROW(Exception::TypeError(String::New(oss.str().c_str())));
}
Local<Function> callback = Local<Function>::Cast(args[cbIndex]);

String::Utf8Value sqlVal(sql);

ExecuteBaton* baton = new ExecuteBaton(connection, *sqlVal, &values, &callback);
ExecuteBaton* baton = new ExecuteBaton(connection, *sqlVal, &values, &options, &callback);
if (baton->error) {
Local<String> message = String::New(baton->error->c_str());
delete baton;
Expand Down Expand Up @@ -732,6 +745,21 @@ Local<Array> Connection::CreateV8ArrayFromRows(ExecuteBaton* baton, vector<colum
return retRows;
}

Local<Array> Connection::CreateV8ArrayFromCols(std::vector<column_t*> columns)
{
Local<Array> v8cols = Array::New(columns.size());
uint32_t index = 0;
for (std::vector<column_t*>::iterator iterator = columns.begin(), end =columns.end(); iterator != end; ++iterator, ++index)
{
column_t* col = *iterator;
v8::Local<v8::Object> v8col = v8::Object::New();
v8col->Set(v8::String::New("name"), String::New(col->name.c_str()));
v8col->Set(v8::String::New("type"), Number::New((double)(col->type)));
v8cols->Set(index, v8col);
}
return v8cols;
}

void Connection::EIO_AfterExecute(uv_work_t* req, int status) {

UNI_SCOPE(scope);
Expand Down Expand Up @@ -761,7 +789,12 @@ void Connection::handleResult(ExecuteBaton* baton, Handle<Value> (&argv)[2]) {
} else {
argv[0] = Undefined();
if(baton->rows) {
argv[1] = CreateV8ArrayFromRows(baton, baton->columns, baton->rows);
Local<Object> obj = CreateV8ArrayFromRows(baton, baton->columns, baton->rows);
if (baton->getColumnMetaData) {
obj->Set(String::New("columnMetaData"),
CreateV8ArrayFromCols(baton->columns));
}
argv[1] = obj;
if (baton->error) goto failed; // delete argv[1] ??
} else {
Local<Object> obj = Object::New();
Expand Down Expand Up @@ -870,10 +903,13 @@ uni::CallbackType Connection::ExecuteSync(const uni::FunctionCallbackInfo& args)

REQ_STRING_ARG(0, sql);
REQ_ARRAY_ARG(1, values);
Local<Object> options;
if (args.Length() > 2 && args[2]->IsObject() && !args[2]->IsFunction())
options = Local<Object>::Cast(args[2]);

String::Utf8Value sqlVal(sql);

ExecuteBaton* baton = new ExecuteBaton(connection, *sqlVal, &values, NULL);
ExecuteBaton* baton = new ExecuteBaton(connection, *sqlVal, &values, &options, NULL);
if (baton->error) {
Local<String> message = String::New(baton->error->c_str());
delete baton;
Expand Down
1 change: 1 addition & 0 deletions src/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class Connection : public ObjectWrap {
private:
static Local<Array> CreateV8ArrayFromRows(ExecuteBaton* baton, std::vector<column_t*> columns, std::vector<row_t*>* rows);
static Local<Object> CreateV8ObjectFromRow(ExecuteBaton* baton, std::vector<column_t*> columns, row_t* currentRow);
static Local<Array> CreateV8ArrayFromCols(std::vector<column_t*> columns);

oracle::occi::Connection* m_connection;
oracle::occi::Environment* m_environment;
Expand Down
7 changes: 6 additions & 1 deletion src/executeBaton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#include <cmath>
using namespace std;

ExecuteBaton::ExecuteBaton(Connection* connection, const char* sql, v8::Local<v8::Array>* values, v8::Handle<v8::Function>* callback) {
ExecuteBaton::ExecuteBaton(Connection* connection, const char* sql, v8::Local<v8::Array>* values, v8::Local<v8::Object>* options, v8::Handle<v8::Function>* callback) {
this->connection = connection;
this->sql = sql;
if(callback!=NULL) {
Expand All @@ -16,6 +16,7 @@ ExecuteBaton::ExecuteBaton(Connection* connection, const char* sql, v8::Local<v8
this->outputs = new std::vector<output_t*>();
this->error = NULL;
if (values) CopyValuesToBaton(this, values);
SetOptionsInBaton(this, options);
this->rows = NULL;
}

Expand Down Expand Up @@ -187,6 +188,10 @@ void ExecuteBaton::CopyValuesToBaton(ExecuteBaton* baton, v8::Local<v8::Array>*
}
}

void ExecuteBaton::SetOptionsInBaton(ExecuteBaton* baton, v8::Local<v8::Object>* options) {
baton->getColumnMetaData = (!options->IsEmpty()) ? (*options)->Get(v8::String::New("getColumnMetaData"))->BooleanValue() : false;
}

void ExecuteBaton::GetVectorParam(ExecuteBaton* baton, arrayParam_t* arrParam, Local<Array> arr) {
// In case the array is empty just initialize the fields as we would need something in Connection::SetValuesOnStatement
if (arr->Length() < 1) {
Expand Down
4 changes: 3 additions & 1 deletion src/executeBaton.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ struct output_t {

class ExecuteBaton {
public:
ExecuteBaton(Connection* connection, const char* sql, v8::Local<v8::Array>* values, v8::Handle<v8::Function>* callback);
ExecuteBaton(Connection* connection, const char* sql, v8::Local<v8::Array>* values, v8::Local<v8::Object>* options, v8::Handle<v8::Function>* callback);
~ExecuteBaton();
void ResetValues();
void ResetRows();
Expand All @@ -84,9 +84,11 @@ class ExecuteBaton {
std::vector<row_t*>* rows;
std::vector<output_t*>* outputs;
std::string* error;
bool getColumnMetaData;
int updateCount;

static void CopyValuesToBaton(ExecuteBaton* baton, v8::Local<v8::Array>* values);
static void SetOptionsInBaton(ExecuteBaton* baton, v8::Local<v8::Object>* options);
static void GetVectorParam(ExecuteBaton* baton, arrayParam_t *value, v8::Local<v8::Array> arr);
};

Expand Down
2 changes: 1 addition & 1 deletion src/statementBaton.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

class StatementBaton : public ExecuteBaton {
public:
StatementBaton(Connection* connection, const char* sql, v8::Local<v8::Array>* values) : ExecuteBaton(connection, sql, values, NULL) {
StatementBaton(Connection* connection, const char* sql, v8::Local<v8::Array>* values) : ExecuteBaton(connection, sql, values, NULL, NULL) {
stmt = NULL;
done = false;
busy = false;
Expand Down
44 changes: 43 additions & 1 deletion test/integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,35 @@ exports['IntegrationTest'] = nodeunit.testCase({
});
},

"select with getColumnMetaData": function(test) {
var self = this;
self.connection.execute("INSERT INTO person (name) VALUES (:1)", ["Bill O'Neil"], function(err, results) {
if(err) { console.error(err); return; }
self.connection.execute("SELECT * FROM person", [], {getColumnMetaData:true}, function(err, results) {
if(err) { console.error(err); return; }
test.equal(results.length, 1);
test.deepEqual(results.columnMetaData, [ { name: 'ID', type: 4 }, { name: 'NAME', type: 3 } ]);
self.connection.execute("SELECT * FROM datatype_test", [], {getColumnMetaData:true}, function(err, results) {
if(err) { console.error(err); return; }
test.equal(results.length, 0);
test.deepEqual(results.columnMetaData,
[ { name: 'ID', type: 4 },
{ name: 'TVARCHAR2', type: 3 },
{ name: 'TNVARCHAR2', type: 3 },
{ name: 'TCHAR', type: 3 },
{ name: 'TNCHAR', type: 3 },
{ name: 'TNUMBER', type: 4 },
{ name: 'TDATE', type: 5 },
{ name: 'TTIMESTAMP', type: 6 },
{ name: 'TCLOB', type: 7 },
{ name: 'TNCLOB', type: 7 },
{ name: 'TBLOB', type: 8 } ]);
test.done();
});
});
});
},

"utf8_chars_in_query": function(test) {
var self = this,
cyrillicString = "тест";
Expand All @@ -209,5 +238,18 @@ exports['IntegrationTest'] = nodeunit.testCase({
test.equal(results[0]['TEST'], cyrillicString, "UTF8 characters in sql query should be preserved");
test.done();
});
}
},

"errors": function(test) {
var self = this;
try { self.connection.execute("SELECT * FROM person", [], "bad arg"); }
catch(e) { test.equal(e.message, "Argument 2 must be a function"); }
try { self.connection.execute("SELECT * FROM person", [], {}); }
catch(e) { test.equal(e.message, "Argument 3 must be a function"); }
try { self.connection.execute("SELECT * FROM person", [], {}, "bad arg"); }
catch(e) { test.equal(e.message, "Argument 3 must be a function"); }

test.done();
},

});
8 changes: 7 additions & 1 deletion test/outparams.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@
outParam1 := 43;
END;
/

CREATE OR REPLACE PROCEDURE procDateTimeOutParam(outParam1 OUT DATE, outParam2 OUT TIMESTAMP)
IS
BEGIN
outParam1 := CURRENT_TIMESTAMP;
outParam2 := CURRENT_TIMESTAMP;
END;
/
CREATE OR REPLACE PROCEDURE procTwoOutParams(param1 IN VARCHAR2, outParam1 OUT NUMBER, outParam2 OUT STRING)
IS
BEGIN
Expand Down