include command metadata with native query result callback - closes #128

This commit is contained in:
brianc 2012-05-30 23:38:03 -05:00
parent 7f00b3ee30
commit 176e6c7ab2
4 changed files with 63 additions and 43 deletions

View File

@ -132,6 +132,14 @@ var clientBuilder = function(config) {
connection._activeQuery.handleRow(row); connection._activeQuery.handleRow(row);
}); });
connection.on('_cmdStatus', function(status) {
var meta = {
};
meta.command = status.command.split(' ')[0];
meta.rowCount = parseInt(status.value);
connection._lastMeta = meta;
});
//TODO: emit more native error properties (make it match js error) //TODO: emit more native error properties (make it match js error)
connection.on('_error', function(err) { connection.on('_error', function(err) {
//create Error object from object literal //create Error object from object literal
@ -156,7 +164,7 @@ var clientBuilder = function(config) {
this._namedQuery = false; this._namedQuery = false;
this._sendQueryPrepared(q.name, q.values||[]); this._sendQueryPrepared(q.name, q.values||[]);
} else { } else {
connection._activeQuery.handleReadyForQuery(); connection._activeQuery.handleReadyForQuery(connection._lastMeta);
connection._activeQuery = null; connection._activeQuery = null;
connection._pulseQueryQueue(); connection._pulseQueryQueue();
} }

View File

@ -82,9 +82,10 @@ p.handleError = function(error) {
} }
} }
p.handleReadyForQuery = function() { p.handleReadyForQuery = function(meta) {
if(this.callback) { if(this.callback) {
this.callback(null, { rows: this.rows }); (meta || {}).rows = this.rows;
this.callback(null, meta);
} }
this.emit('end'); this.emit('end');
}; };

View File

@ -30,6 +30,7 @@ static Persistent<String> type_symbol;
static Persistent<String> channel_symbol; static Persistent<String> channel_symbol;
static Persistent<String> payload_symbol; static Persistent<String> payload_symbol;
static Persistent<String> emit_symbol; static Persistent<String> emit_symbol;
static Persistent<String> command_symbol;
class Connection : public ObjectWrap { class Connection : public ObjectWrap {
@ -62,6 +63,7 @@ public:
type_symbol = NODE_PSYMBOL("type"); type_symbol = NODE_PSYMBOL("type");
channel_symbol = NODE_PSYMBOL("channel"); channel_symbol = NODE_PSYMBOL("channel");
payload_symbol = NODE_PSYMBOL("payload"); payload_symbol = NODE_PSYMBOL("payload");
command_symbol = NODE_PSYMBOL("command");
NODE_SET_PROTOTYPE_METHOD(t, "connect", Connect); NODE_SET_PROTOTYPE_METHOD(t, "connect", Connect);
@ -437,19 +439,22 @@ protected:
} }
} }
void HandleResult(const PGresult* result) void HandleResult(PGresult* result)
{ {
ExecStatusType status = PQresultStatus(result); ExecStatusType status = PQresultStatus(result);
switch(status) { switch(status) {
case PGRES_TUPLES_OK: case PGRES_TUPLES_OK:
HandleTuplesResult(result); {
HandleTuplesResult(result);
EmitCommandMetaData(result);
}
break; break;
case PGRES_FATAL_ERROR: case PGRES_FATAL_ERROR:
HandleErrorResult(result); HandleErrorResult(result);
break; break;
case PGRES_COMMAND_OK: case PGRES_COMMAND_OK:
case PGRES_EMPTY_QUERY: case PGRES_EMPTY_QUERY:
//do nothing EmitCommandMetaData(result);
break; break;
default: default:
printf("Unrecogized query status: %s\n", PQresStatus(status)); printf("Unrecogized query status: %s\n", PQresStatus(status));
@ -457,12 +462,23 @@ protected:
} }
} }
void EmitCommandMetaData(PGresult* result)
{
HandleScope scope;
Local<Object> info = Object::New();
info->Set(command_symbol, String::New(PQcmdStatus(result)));
info->Set(value_symbol, String::New(PQcmdTuples(result)));
Handle<Value> e = (Handle<Value>)info;
Emit("_cmdStatus", &e);
}
//maps the postgres tuple results to v8 objects //maps the postgres tuple results to v8 objects
//and emits row events //and emits row events
//TODO look at emitting fewer events because the back & forth between //TODO look at emitting fewer events because the back & forth between
//javascript & c++ might introduce overhead (requires benchmarking) //javascript & c++ might introduce overhead (requires benchmarking)
void HandleTuplesResult(const PGresult* result) void HandleTuplesResult(const PGresult* result)
{ {
HandleScope scope;
int rowCount = PQntuples(result); int rowCount = PQntuples(result);
for(int rowNumber = 0; rowNumber < rowCount; rowNumber++) { for(int rowNumber = 0; rowNumber < rowCount; rowNumber++) {
//create result object for this row //create result object for this row
@ -489,7 +505,6 @@ protected:
row->Set(Integer::New(fieldNumber), field); row->Set(Integer::New(fieldNumber), field);
} }
//not sure about what to dealloc or scope#Close here
Handle<Value> e = (Handle<Value>)row; Handle<Value> e = (Handle<Value>)row;
Emit("_row", &e); Emit("_row", &e);
} }
@ -564,30 +579,30 @@ private:
{ {
PostgresPollingStatusType status = PQconnectPoll(connection_); PostgresPollingStatusType status = PQconnectPoll(connection_);
switch(status) { switch(status) {
case PGRES_POLLING_READING: case PGRES_POLLING_READING:
TRACE("Polled: PGRES_POLLING_READING"); TRACE("Polled: PGRES_POLLING_READING");
StopWrite(); StopWrite();
StartRead(); StartRead();
break; break;
case PGRES_POLLING_WRITING: case PGRES_POLLING_WRITING:
TRACE("Polled: PGRES_POLLING_WRITING"); TRACE("Polled: PGRES_POLLING_WRITING");
StopRead(); StopRead();
StartWrite(); StartWrite();
break; break;
case PGRES_POLLING_FAILED: case PGRES_POLLING_FAILED:
StopRead(); StopRead();
StopWrite(); StopWrite();
TRACE("Polled: PGRES_POLLING_FAILED"); TRACE("Polled: PGRES_POLLING_FAILED");
EmitLastError(); EmitLastError();
break; break;
case PGRES_POLLING_OK: case PGRES_POLLING_OK:
TRACE("Polled: PGRES_POLLING_OK"); TRACE("Polled: PGRES_POLLING_OK");
connecting_ = false; connecting_ = false;
StartRead(); StartRead();
Emit("connect"); Emit("connect");
default: default:
//printf("Unknown polling status: %d\n", status); //printf("Unknown polling status: %d\n", status);
break; break;
} }
} }

View File

@ -2,25 +2,21 @@ var helper = require(__dirname + "/test-helper");
var pg = helper.pg; var pg = helper.pg;
test('should return insert metadata', function() { test('should return insert metadata', function() {
return false;
pg.connect(helper.config, assert.calls(function(err, client) { pg.connect(helper.config, assert.calls(function(err, client) {
assert.isNull(err); assert.isNull(err);
client.query("CREATE TEMP TABLE zugzug(name varchar(10))", assert.calls(function(err, result) { client.query("CREATE TEMP TABLE zugzug(name varchar(10))", assert.calls(function(err, result) {
assert.isNull(err); assert.isNull(err);
//let's list this as ignored for now
// process.nextTick(function() {
// test('should identify "CREATE TABLE" message', function() {
// return false;
// assert.equal(result.command, "CREATE TABLE");
// assert.equal(result.rowCount, 0);
// })
// })
assert.equal(result.oid, null); assert.equal(result.oid, null);
assert.equal(result.command, 'CREATE');
client.query("INSERT INTO zugzug(name) VALUES('more work?')", assert.calls(function(err, result) { client.query("INSERT INTO zugzug(name) VALUES('more work?')", assert.calls(function(err, result) {
assert.equal(result.command, "INSERT"); assert.equal(result.command, "INSERT");
assert.equal(result.rowCount, 1); assert.equal(result.rowCount, 1);
process.nextTick(client.end.bind(client)); client.query('SELECT * FROM zugzug', assert.calls(function(err, result) {
return false; assert.isNull(err);
assert.equal(result.rowCount, 1);
assert.equal(result.command, 'SELECT');
process.nextTick(pg.end.bind(pg));
}))
})) }))
})) }))
})) }))