Add field metadata to query result object
Refactored the way rows are built in the native bindings which should result in a small performance improvement
This commit is contained in:
parent
05e9026aea
commit
3f96bbbc5c
@ -171,6 +171,10 @@ var clientBuilder = function(config) {
|
||||
connection._pulseQueryQueue(true);
|
||||
});
|
||||
|
||||
connection.on('_rowDescription', function(rowDescription) {
|
||||
connection._activeQuery.handleRowDescription(rowDescription);
|
||||
});
|
||||
|
||||
//proxy some events to active query
|
||||
connection.on('_row', function(row) {
|
||||
connection._activeQuery.handleRow(row);
|
||||
|
@ -22,6 +22,7 @@ var NativeQuery = function(config, values, callback) {
|
||||
this.callback = c.callback;
|
||||
|
||||
this._result = new Result();
|
||||
this._addedFields = false;
|
||||
//normalize values
|
||||
if(this.values) {
|
||||
for(var i = 0, len = this.values.length; i < len; i++) {
|
||||
@ -39,13 +40,35 @@ var mapRowData = function(row) {
|
||||
for(var i = 0, len = row.length; i < len; i++) {
|
||||
var item = row[i];
|
||||
result[item.name] = item.value === null ? null :
|
||||
types.getTypeParser(item.type, 'text')(item.value);
|
||||
types.getTypeParser(item.dataTypeID, 'text')(item.value);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
NativeQuery.prototype.handleRowDescription = function(rowDescription) {
|
||||
//multiple query statements in 1 action can result in multiple sets
|
||||
//of rowDescriptions...eg: 'select NOW(); select 1::int;'
|
||||
if(this._result.fields.length) {
|
||||
this._result.fields = [];
|
||||
}
|
||||
for(var i = 0, len = rowDescription.length; i < len; i++) {
|
||||
this._result.addField(rowDescription[i]);
|
||||
}
|
||||
};
|
||||
|
||||
NativeQuery.prototype.handleRow = function(rowData) {
|
||||
var row = mapRowData(rowData);
|
||||
var row = {};
|
||||
for(var i = 0, len = rowData.length; i < len; i++) {
|
||||
var rawValue = rowData[i];
|
||||
var field = this._result.fields[i];
|
||||
var fieldType = field.dataTypeID;
|
||||
var parsedValue = null;
|
||||
if(rawValue !== null) {
|
||||
parsedValue = types.getTypeParser(fieldType, 'text')(rawValue);
|
||||
}
|
||||
var fieldName = field.name;
|
||||
row[fieldName] = parsedValue;
|
||||
}
|
||||
if(this.callback) {
|
||||
this._result.addRow(row);
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ public:
|
||||
routine_symbol = NODE_PSYMBOL("routine");
|
||||
name_symbol = NODE_PSYMBOL("name");
|
||||
value_symbol = NODE_PSYMBOL("value");
|
||||
type_symbol = NODE_PSYMBOL("type");
|
||||
type_symbol = NODE_PSYMBOL("dataTypeID");
|
||||
channel_symbol = NODE_PSYMBOL("channel");
|
||||
payload_symbol = NODE_PSYMBOL("payload");
|
||||
command_symbol = NODE_PSYMBOL("command");
|
||||
@ -522,6 +522,33 @@ protected:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//maps the postgres tuple results to v8 objects
|
||||
//and emits row events
|
||||
//TODO look at emitting fewer events because the back & forth between
|
||||
//javascript & c++ might introduce overhead (requires benchmarking)
|
||||
void EmitRowDescription(const PGresult* result)
|
||||
{
|
||||
HandleScope scope;
|
||||
Local<Array> row = Array::New();
|
||||
int fieldCount = PQnfields(result);
|
||||
for(int fieldNumber = 0; fieldNumber < fieldCount; fieldNumber++) {
|
||||
Local<Object> field = Object::New();
|
||||
//name of field
|
||||
char* fieldName = PQfname(result, fieldNumber);
|
||||
field->Set(name_symbol, String::New(fieldName));
|
||||
|
||||
//oid of type of field
|
||||
int fieldType = PQftype(result, fieldNumber);
|
||||
field->Set(type_symbol, Integer::New(fieldType));
|
||||
|
||||
row->Set(Integer::New(fieldNumber), field);
|
||||
}
|
||||
|
||||
Handle<Value> e = (Handle<Value>)row;
|
||||
Emit("_rowDescription", &e);
|
||||
}
|
||||
|
||||
bool HandleResult(PGresult* result)
|
||||
{
|
||||
TRACE("PQresultStatus");
|
||||
@ -529,6 +556,7 @@ protected:
|
||||
switch(status) {
|
||||
case PGRES_TUPLES_OK:
|
||||
{
|
||||
EmitRowDescription(result);
|
||||
HandleTuplesResult(result);
|
||||
EmitCommandMetaData(result);
|
||||
return true;
|
||||
@ -592,24 +620,14 @@ protected:
|
||||
Local<Array> row = Array::New();
|
||||
int fieldCount = PQnfields(result);
|
||||
for(int fieldNumber = 0; fieldNumber < fieldCount; fieldNumber++) {
|
||||
Local<Object> field = Object::New();
|
||||
//name of field
|
||||
char* fieldName = PQfname(result, fieldNumber);
|
||||
field->Set(name_symbol, String::New(fieldName));
|
||||
|
||||
//oid of type of field
|
||||
int fieldType = PQftype(result, fieldNumber);
|
||||
field->Set(type_symbol, Integer::New(fieldType));
|
||||
|
||||
//value of field
|
||||
if(PQgetisnull(result, rowNumber, fieldNumber)) {
|
||||
field->Set(value_symbol, Null());
|
||||
row->Set(Integer::New(fieldNumber), Null());
|
||||
} else {
|
||||
char* fieldValue = PQgetvalue(result, rowNumber, fieldNumber);
|
||||
field->Set(value_symbol, String::New(fieldValue));
|
||||
row->Set(Integer::New(fieldNumber), String::New(fieldValue));
|
||||
}
|
||||
|
||||
row->Set(Integer::New(fieldNumber), field);
|
||||
}
|
||||
|
||||
Handle<Value> e = (Handle<Value>)row;
|
||||
|
37
test/integration/client/row-description-on-results-tests.js
Normal file
37
test/integration/client/row-description-on-results-tests.js
Normal file
@ -0,0 +1,37 @@
|
||||
var helper = require('./test-helper');
|
||||
|
||||
var Client = helper.Client;
|
||||
|
||||
var conInfo = helper.config;
|
||||
|
||||
var checkResult = function(result) {
|
||||
assert(result.fields);
|
||||
assert.equal(result.fields.length, 3);
|
||||
var fields = result.fields;
|
||||
assert.equal(fields[0].name, 'now');
|
||||
assert.equal(fields[1].name, 'num');
|
||||
assert.equal(fields[2].name, 'texty');
|
||||
assert.equal(fields[0].dataTypeID, 1184);
|
||||
assert.equal(fields[1].dataTypeID, 23);
|
||||
assert.equal(fields[2].dataTypeID, 25);
|
||||
};
|
||||
|
||||
test('row descriptions on result object', function() {
|
||||
var client = new Client(conInfo);
|
||||
client.connect(assert.success(function() {
|
||||
client.query('SELECT NOW() as now, 1::int as num, $1::text as texty', ["hello"], assert.success(function(result) {
|
||||
checkResult(result);
|
||||
client.end();
|
||||
}));
|
||||
}));
|
||||
});
|
||||
|
||||
test('row description on no rows', function() {
|
||||
var client = new Client(conInfo);
|
||||
client.connect(assert.success(function() {
|
||||
client.query('SELECT NOW() as now, 1::int as num, $1::text as texty LIMIT 0', ["hello"], assert.success(function(result) {
|
||||
checkResult(result);
|
||||
client.end();
|
||||
}));
|
||||
}));
|
||||
});
|
Loading…
Reference in New Issue
Block a user