Skip to content

Commit 3f96bbb

Browse files
committed
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
1 parent 05e9026 commit 3f96bbb

File tree

4 files changed

+97
-15
lines changed

4 files changed

+97
-15
lines changed

lib/native/index.js

+4
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ var clientBuilder = function(config) {
171171
connection._pulseQueryQueue(true);
172172
});
173173

174+
connection.on('_rowDescription', function(rowDescription) {
175+
connection._activeQuery.handleRowDescription(rowDescription);
176+
});
177+
174178
//proxy some events to active query
175179
connection.on('_row', function(row) {
176180
connection._activeQuery.handleRow(row);

lib/native/query.js

+25-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var NativeQuery = function(config, values, callback) {
2222
this.callback = c.callback;
2323

2424
this._result = new Result();
25+
this._addedFields = false;
2526
//normalize values
2627
if(this.values) {
2728
for(var i = 0, len = this.values.length; i < len; i++) {
@@ -39,13 +40,35 @@ var mapRowData = function(row) {
3940
for(var i = 0, len = row.length; i < len; i++) {
4041
var item = row[i];
4142
result[item.name] = item.value === null ? null :
42-
types.getTypeParser(item.type, 'text')(item.value);
43+
types.getTypeParser(item.dataTypeID, 'text')(item.value);
4344
}
4445
return result;
4546
};
4647

48+
NativeQuery.prototype.handleRowDescription = function(rowDescription) {
49+
//multiple query statements in 1 action can result in multiple sets
50+
//of rowDescriptions...eg: 'select NOW(); select 1::int;'
51+
if(this._result.fields.length) {
52+
this._result.fields = [];
53+
}
54+
for(var i = 0, len = rowDescription.length; i < len; i++) {
55+
this._result.addField(rowDescription[i]);
56+
}
57+
};
58+
4759
NativeQuery.prototype.handleRow = function(rowData) {
48-
var row = mapRowData(rowData);
60+
var row = {};
61+
for(var i = 0, len = rowData.length; i < len; i++) {
62+
var rawValue = rowData[i];
63+
var field = this._result.fields[i];
64+
var fieldType = field.dataTypeID;
65+
var parsedValue = null;
66+
if(rawValue !== null) {
67+
parsedValue = types.getTypeParser(fieldType, 'text')(rawValue);
68+
}
69+
var fieldName = field.name;
70+
row[fieldName] = parsedValue;
71+
}
4972
if(this.callback) {
5073
this._result.addRow(row);
5174
}

src/binding.cc

+31-13
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class Connection : public ObjectWrap {
6161
routine_symbol = NODE_PSYMBOL("routine");
6262
name_symbol = NODE_PSYMBOL("name");
6363
value_symbol = NODE_PSYMBOL("value");
64-
type_symbol = NODE_PSYMBOL("type");
64+
type_symbol = NODE_PSYMBOL("dataTypeID");
6565
channel_symbol = NODE_PSYMBOL("channel");
6666
payload_symbol = NODE_PSYMBOL("payload");
6767
command_symbol = NODE_PSYMBOL("command");
@@ -522,13 +522,41 @@ class Connection : public ObjectWrap {
522522
}
523523
return false;
524524
}
525+
526+
//maps the postgres tuple results to v8 objects
527+
//and emits row events
528+
//TODO look at emitting fewer events because the back & forth between
529+
//javascript & c++ might introduce overhead (requires benchmarking)
530+
void EmitRowDescription(const PGresult* result)
531+
{
532+
HandleScope scope;
533+
Local<Array> row = Array::New();
534+
int fieldCount = PQnfields(result);
535+
for(int fieldNumber = 0; fieldNumber < fieldCount; fieldNumber++) {
536+
Local<Object> field = Object::New();
537+
//name of field
538+
char* fieldName = PQfname(result, fieldNumber);
539+
field->Set(name_symbol, String::New(fieldName));
540+
541+
//oid of type of field
542+
int fieldType = PQftype(result, fieldNumber);
543+
field->Set(type_symbol, Integer::New(fieldType));
544+
545+
row->Set(Integer::New(fieldNumber), field);
546+
}
547+
548+
Handle<Value> e = (Handle<Value>)row;
549+
Emit("_rowDescription", &e);
550+
}
551+
525552
bool HandleResult(PGresult* result)
526553
{
527554
TRACE("PQresultStatus");
528555
ExecStatusType status = PQresultStatus(result);
529556
switch(status) {
530557
case PGRES_TUPLES_OK:
531558
{
559+
EmitRowDescription(result);
532560
HandleTuplesResult(result);
533561
EmitCommandMetaData(result);
534562
return true;
@@ -592,24 +620,14 @@ class Connection : public ObjectWrap {
592620
Local<Array> row = Array::New();
593621
int fieldCount = PQnfields(result);
594622
for(int fieldNumber = 0; fieldNumber < fieldCount; fieldNumber++) {
595-
Local<Object> field = Object::New();
596-
//name of field
597-
char* fieldName = PQfname(result, fieldNumber);
598-
field->Set(name_symbol, String::New(fieldName));
599-
600-
//oid of type of field
601-
int fieldType = PQftype(result, fieldNumber);
602-
field->Set(type_symbol, Integer::New(fieldType));
603623

604624
//value of field
605625
if(PQgetisnull(result, rowNumber, fieldNumber)) {
606-
field->Set(value_symbol, Null());
626+
row->Set(Integer::New(fieldNumber), Null());
607627
} else {
608628
char* fieldValue = PQgetvalue(result, rowNumber, fieldNumber);
609-
field->Set(value_symbol, String::New(fieldValue));
629+
row->Set(Integer::New(fieldNumber), String::New(fieldValue));
610630
}
611-
612-
row->Set(Integer::New(fieldNumber), field);
613631
}
614632

615633
Handle<Value> e = (Handle<Value>)row;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
var helper = require('./test-helper');
2+
3+
var Client = helper.Client;
4+
5+
var conInfo = helper.config;
6+
7+
var checkResult = function(result) {
8+
assert(result.fields);
9+
assert.equal(result.fields.length, 3);
10+
var fields = result.fields;
11+
assert.equal(fields[0].name, 'now');
12+
assert.equal(fields[1].name, 'num');
13+
assert.equal(fields[2].name, 'texty');
14+
assert.equal(fields[0].dataTypeID, 1184);
15+
assert.equal(fields[1].dataTypeID, 23);
16+
assert.equal(fields[2].dataTypeID, 25);
17+
};
18+
19+
test('row descriptions on result object', function() {
20+
var client = new Client(conInfo);
21+
client.connect(assert.success(function() {
22+
client.query('SELECT NOW() as now, 1::int as num, $1::text as texty', ["hello"], assert.success(function(result) {
23+
checkResult(result);
24+
client.end();
25+
}));
26+
}));
27+
});
28+
29+
test('row description on no rows', function() {
30+
var client = new Client(conInfo);
31+
client.connect(assert.success(function() {
32+
client.query('SELECT NOW() as now, 1::int as num, $1::text as texty LIMIT 0', ["hello"], assert.success(function(result) {
33+
checkResult(result);
34+
client.end();
35+
}));
36+
}));
37+
});

0 commit comments

Comments
 (0)