diff --git a/lib/client.js b/lib/client.js index 17d72601..4dfe893c 100644 --- a/lib/client.js +++ b/lib/client.js @@ -147,6 +147,18 @@ p.bind = function(config) { this.send('B', buffer.join()); }; +p.execute = function(name, rows) { + this.send('E',new BufferList().addCString(name||'').addInt32(rows||0).join()); +}; + +p.flush = function() { + this.send('H',Buffer(0)); +} + +p.sync = function() { + this.send('S', Buffer(0)); +}; + p.pulseQueryQueue = function(ready) { if(!this.readyForQuery) { return; @@ -255,11 +267,13 @@ var messageNames = { Z: 'readyForQuery', T: 'rowDescription', D: 'dataRow', - E: 'error' + E: 'error', + 1: 'parseComplete', + 2: 'bindComplete' }; p.parseMessage = function() { - var remaining = this.buffer.length - this.offset - 1; + var remaining = this.buffer.length - (this.offset); if(remaining < 5) { //cannot read id + length without at least 5 bytes //just abort the read now @@ -276,7 +290,7 @@ p.parseMessage = function() { length: this.parseInt32() }; - if(remaining < message.length) { + if(remaining <= message.length) { this.lastBuffer = this.buffer; //rewind the last 5 bytes we read this.lastOffset = this.offset-5; @@ -365,6 +379,7 @@ p.parseD = function(msg) { return msg; }; +//parses error p.parseE = function(msg) { var fields = {}; var fieldType = this.readString(1); @@ -387,6 +402,16 @@ p.parseE = function(msg) { return msg; }; +//parses parseComplete +p.parse1 = function(msg) { + return msg; +}; + +//parses bindComplete +p.parse2 = function(msg) { + return msg; +}; + p.readChar = function() { return Buffer([this.buffer[this.offset++]]).toString(this.encoding); }; diff --git a/test/integration/simple-bound-query-tests.js b/test/integration/simple-bound-query-tests.js new file mode 100644 index 00000000..c91fa4a6 --- /dev/null +++ b/test/integration/simple-bound-query-tests.js @@ -0,0 +1,34 @@ +var helper = require(__dirname + '/test-helper'); +http://developer.postgresql.org/pgdocs/postgres/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY + +helper.connect(function(con) { + + con.parse({ + text: 'select * from ids' + }); + con.flush(); + + con.once('parseComplete', function() { + con.bind(); + con.flush(); + }); + + con.once('bindComplete', function() { + con.execute(); + con.flush(); + }); + + con.on('dataRow', function(msg) { + sys.debug("got row from pepared query"); + }); + + con.on('commandComplete', function() { + con.sync(); + }); + + con.on('readyForQuery', function() { + con.end(); + }); + +}); + diff --git a/test/integration/simple-query-tests.js b/test/integration/simple-query-tests.js index fcbf7688..b82b11a8 100644 --- a/test/integration/simple-query-tests.js +++ b/test/integration/simple-query-tests.js @@ -1,27 +1,24 @@ var helper = require(__dirname+"/test-helper"); var assert = require('assert'); -var client = helper.client(); + var rows = []; //testing the low level 1-1 mapping api of client to postgres messages //it's cumbersome to use the api this way -client.query('create temporary table bang(id integer)'); -client.once('commandComplete', function() { - client.query('insert into bang(id) values(1)'); - client.once('commandComplete', function() { - client.query('select * from bang'); - client.on('dataRow', function(row) { - rows.push(row.fields); - }); - client.on('readyForQuery',function() { - client.end(); - }); +helper.connect(function(con) { + con.query('select * from ids'); + con.on('dataRow', function(msg) { + rows.push(msg.fields); + }); + con.once('readyForQuery', function() { + con.end(); }); }); process.on('exit', function() { - assert.equal(rows.length, 1); + assert.equal(rows.length, 2); assert.equal(rows[0].length, 1); - assert.equal(rows[0], 1); + assert.equal(rows[0] [0], 1); + assert.equal(rows[1] [0], 2); }); diff --git a/test/integration/test-helper.js b/test/integration/test-helper.js index 5d352456..5e1cce2c 100644 --- a/test/integration/test-helper.js +++ b/test/integration/test-helper.js @@ -2,15 +2,23 @@ Client = require(__dirname+'/../../lib/client'); sys = require('sys'); //creates a configured, connecting client -var client = function() { - var client = new Client({ +var connect = function(onReady) { + var con = new Client({ database: 'postgres', user: 'brian' }); - client.connect(); - return client; + con.connect(); + con.once('readyForQuery', function() { + con.query('create temporary table ids(id integer)'); + con.once('readyForQuery', function() { + con.query('insert into ids(id) values(1); insert into ids(id) values(2);'); + con.once('readyForQuery',function() { + onReady(con); + }); + }); + }); }; module.exports = { - client: client + connect: connect }; diff --git a/test/unit/parser-tests.js b/test/unit/parser-tests.js index 578e4e5d..8a0e9f38 100644 --- a/test/unit/parser-tests.js +++ b/test/unit/parser-tests.js @@ -9,7 +9,8 @@ var paramStatusBuffer = buffers.parameterStatus('client_encoding', 'UTF8'); var readyForQueryBuffer = buffers.readyForQuery(); var backendKeyDataBuffer = buffers.backendKeyData(1,2); var commandCompleteBuffer = buffers.commandComplete("SELECT 3"); - +var parseCompleteBuffer = buffers.parseComplete(); +var bindCompleteBuffer = buffers.bindComplete(); var addRow = function(bufferList, name, offset) { return bufferList.addCString(name) //field name @@ -294,6 +295,20 @@ test('Client', function() { }); }); }); + + test('parses parse complete command', function() { + testForMessage(parseCompleteBuffer, { + id: '1', + name: 'parseComplete' + }); + }); + + test('parses bind complete command', function() { + testForMessage(bindCompleteBuffer, { + id: '2', + name: 'bindComplete' + }); + }); }); //since the data message on a stream can randomly divide the incomming diff --git a/test/unit/prepared-query-tests.js b/test/unit/prepared-query-tests.js index ec125e8f..bd4fc7a7 100644 --- a/test/unit/prepared-query-tests.js +++ b/test/unit/prepared-query-tests.js @@ -40,9 +40,9 @@ test('prepared queries', function() { //server raises parse complete message test('sends bind message', function() { - + test('binding to unnamed prepared statement with no values', function() { - + client.bind(); assert.length(client.stream.packets, 1); var packet = client.stream.packets.pop(); @@ -57,8 +57,35 @@ test('prepared queries', function() { }); - test('recieves rows', function() { - return false; + test('sends execute message', function() { + test('executing an unnamed portal with no row limit', function() { + client.execute(); + assert.length(client.stream.packets, 1); + var packet = client.stream.packets.pop(); + var expectedBuffer = new BufferList() + .addCString('') + .addInt32(0) + .join(true,'E'); + assert.equalBuffers(packet, expectedBuffer); + + }); + + }); + + test('sends flush command', function() { + client.flush(); + assert.length(client.stream.packets, 1); + var packet = client.stream.packets.pop(); + var expected = new BufferList().join(true, 'H'); + assert.equalBuffers(packet, expected); + }); + + test('sends sync command', function() { + client.sync(); + assert.length(client.stream.packets, 1); + var packet = client.stream.packets.pop(); + var expected = new BufferList().join(true,'S'); + assert.equalBuffers(packet, expected); }); }); diff --git a/test/unit/test-buffers.js b/test/unit/test-buffers.js index f14674e1..ae9c9db2 100644 --- a/test/unit/test-buffers.js +++ b/test/unit/test-buffers.js @@ -81,4 +81,8 @@ buffers.parseComplete = function() { return new BufferList().join(true, '1'); }; +buffers.bindComplete = function() { + return new BufferList().join(true, '2'); +}; + module.exports = buffers;