diff --git a/lib/client.js b/lib/client.js index 3815dc79..35d97b0a 100644 --- a/lib/client.js +++ b/lib/client.js @@ -105,14 +105,14 @@ var Query = function(config) { sys.inherits(Query, EventEmitter); var p = Query.prototype; -p.isBoundCommand = function() { +p.requiresPreparation = function() { return (this.values || 0).length > 0 || this.name || this.rows; }; p.submit = function(connection) { var self = this; - if(this.isBoundCommand()) { - throw new Error('Bound command not supported yet! :('); + if(this.requiresPreparation()) { + this.prepare(connection); } else { connection.query(this.text); } @@ -132,6 +132,52 @@ p.submit = function(connection) { }); }; +p.prepare = function(connection) { + var self = this; + + connection.parse({ + text: self.text, + name: self.name, + types: self.types + }); + connection.flush(); + + var onParseComplete = function() { + connection.bind({ + portal: self.name, + statement: self.name, + values: self.values + }); + connection.flush(); + }; + + connection.once('parseComplete', onParseComplete); + + var onBindComplete = function() { + connection.describe({ + type: 'P', + name: self.name || "" + }); + //http://developer.postgresql.org/pgdocs/postgres/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY + //TODO get ourselves a rowDescription for result type coercion + connection.execute({ + portal: self.name, + rows: self.rows + }); + connection.flush(); + }; + + connection.once('bindComplete', onBindComplete); + + //TODO support EmptyQueryResponse, ErrorResponse, and PortalSuspended + var onCommandComplete = function() { + connection.sync(); + }; + connection.once('commandComplete', onCommandComplete); + +}; + + p.onRowDescription = function(msg) { var typeIds = msg.fields.map(function(field) { return field.dataTypeID; diff --git a/lib/connection.js b/lib/connection.js index 9a17f166..ef390c46 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -165,7 +165,9 @@ p.end = function() { }; p.describe = function(msg) { - this.send('D',Buffer(msg.type+msg.name+'\0')); + var str = msg.type + (msg.name || "" ) + '\0'; + var buffer = Buffer(str, this.encoding); + this.send('D', buffer); }; //parsing methods diff --git a/test/integration/client/prepared-statement-tests.js b/test/integration/client/prepared-statement-tests.js index e69de29b..7d48869f 100644 --- a/test/integration/client/prepared-statement-tests.js +++ b/test/integration/client/prepared-statement-tests.js @@ -0,0 +1,20 @@ +var helper = require(__dirname +'/test-helper'); + +test("simple prepared statement", function(){ + var client = helper.client(); + client.connection.on('message', function(msg) { + console.log(msg.name); + }); + var query = client.query({ + text: 'select age from person where name = $1', + values: ['Brian'] + }); + + assert.raises(query, 'row', function(row) { + assert.equal(row.fields[0], 20); + }); + + assert.raises(query, 'end', function() { + client.end(); + }); +}); diff --git a/test/unit/connection/outbound-sending-tests.js b/test/unit/connection/outbound-sending-tests.js index 1bb77ec9..7451f297 100644 --- a/test/unit/connection/outbound-sending-tests.js +++ b/test/unit/connection/outbound-sending-tests.js @@ -158,8 +158,14 @@ test('sends end command', function() { test('sends describe command',function() { test('describe statement', function() { - con.describe({type: 's', name: 'bang'}); - var expected = new BufferList().addChar('s').addCString('bang').join(true, 'D') + con.describe({type: 'S', name: 'bang'}); + var expected = new BufferList().addChar('S').addCString('bang').join(true, 'D') + assert.recieved(stream, expected); + }); + + test("describe unnamed portal", function() { + con.describe({type: 'P'}); + var expected = new BufferList().addChar('P').addCString("").join(true, "D"); assert.recieved(stream, expected); }); });