From 95ec1b403c445a1357117ada54b750b10a50758d Mon Sep 17 00:00:00 2001 From: brianc Date: Sat, 23 Oct 2010 14:50:28 -0500 Subject: [PATCH] parsing of prepared queries with no parameters --- {test/unit => lib}/buffer-list.js | 15 ++++----- lib/client.js | 52 ++++++++++++++++++++++++------- test/unit/prepared-query-tests.js | 26 ++++++++++++++-- test/unit/test-helper.js | 2 +- 4 files changed, 73 insertions(+), 22 deletions(-) rename {test/unit => lib}/buffer-list.js (74%) diff --git a/test/unit/buffer-list.js b/lib/buffer-list.js similarity index 74% rename from test/unit/buffer-list.js rename to lib/buffer-list.js index 07ceeea0..d27a0bea 100644 --- a/test/unit/buffer-list.js +++ b/lib/buffer-list.js @@ -1,23 +1,24 @@ BufferList = function() { this.buffers = []; }; +var p = BufferList.prototype; -BufferList.prototype.add = function(buffer, front) { +p.add = function(buffer, front) { this.buffers[front ? "unshift" : "push"](buffer); return this; }; -BufferList.prototype.addInt16 = function(val, front) { +p.addInt16 = function(val, front) { return this.add(Buffer([(val >>> 8),(val >>> 0)]),front); }; -BufferList.prototype.getByteLength = function(initial) { +p.getByteLength = function(initial) { return this.buffers.reduce(function(previous, current){ return previous + current.length; },initial || 0); }; -BufferList.prototype.addInt32 = function(val, first) { +p.addInt32 = function(val, first) { return this.add(Buffer([ (val >>> 24 & 0xFF), (val >>> 16 & 0xFF), @@ -26,15 +27,15 @@ BufferList.prototype.addInt32 = function(val, first) { ]),first); }; -BufferList.prototype.addCString = function(val) { +p.addCString = function(val) { return this.add(Buffer(val + '\0','utf8')); }; -BufferList.prototype.addChar = function(char, first) { +p.addChar = function(char, first) { return this.add(Buffer(char,'utf8'), first); }; -BufferList.prototype.join = function(appendLength, char) { +p.join = function(appendLength, char) { var length = this.getByteLength(); if(appendLength) { this.addInt32(length+4, true); diff --git a/lib/client.js b/lib/client.js index 7e6baef8..0ef22a95 100644 --- a/lib/client.js +++ b/lib/client.js @@ -4,6 +4,7 @@ var crypto = require('crypto'); var EventEmitter = require('events').EventEmitter; var utils = require(__dirname + '/utils'); +var BufferList = require(__dirname + '/buffer-list'); var Client = function(config) { EventEmitter.call(this); @@ -28,7 +29,7 @@ sys.inherits(Client, EventEmitter); var p = Client.prototype; p.connect = function() { - if(this.stream.readyState == 'closed'){ + if(this.stream.readyState === 'closed'){ this.stream.connect(this.port, this.host); } var self = this; @@ -109,8 +110,23 @@ p.query = function(text) { return this; }; -p.parse = function(text, types) { - +p.parse = function(query) { + //expect something like this: + // { name: 'queryName', + // text: 'select * from blah', + // types: ['int8', 'bool'] } + + //normalize missing query names to allow for null + query.name = query.name || ''; + //normalize null type array + query.types = query.types || []; + + //append internal type identifier + query.type = 'preparedQuery'; + + this.queryQueue.push(query); + this.pulseQueryQueue(); + return this; }; p.pulseQueryQueue = function(ready) { @@ -121,10 +137,22 @@ p.pulseQueryQueue = function(ready) { if(query) { var self = this; this.readyForQuery = false; - if(query.type !== 'simpleQuery') { - throw new Error("do not know how to handle this"); + if(query.type === 'simpleQuery') { + this.send('Q', new Buffer(query.text + '\0', this.encoding)); + return; } - this.send('Q', new Buffer(query.text + '\0', this.encoding)); + if(query.type === 'preparedQuery') { + var buffer = new BufferList() + .addCString(query.name) //name of query + .addCString(query.text) //actual query text + .addInt16(0).join(); + if(query.types.length) { + sys.debug("Typed parameters not supported yet!"); + } + this.send('P', buffer); + return; + } + throw new Error("I don't know this query type " + sys.inspect(query)); } }; //query handling @@ -242,16 +270,16 @@ p.parseMessage = function() { p.parseR = function(msg) { var code = 0; - if(msg.length == 8) { + if(msg.length === 8) { code = this.parseInt32(); - if(code == 3) { + if(code === 3) { msg.name = 'authenticationCleartextPassword'; } return msg; } - if(msg.length == 12) { + if(msg.length === 12) { code = this.parseInt32(); - if(code == 5) { //md5 required + if(code === 5) { //md5 required msg.name = 'authenticationMD5Password'; msg.salt = new Buffer(4); this.buffer.copy(msg.salt, 0, this.offset, this.offset + 4); @@ -302,7 +330,7 @@ p.parseField = function() { dataTypeID: this.parseInt32(), dataTypeSize: this.parseInt16(), dataTypeModifier: this.parseInt32(), - format: this.parseInt16() == 0 ? 'text' : 'binary' + format: this.parseInt16() === 0 ? 'text' : 'binary' }; return field; }; @@ -312,7 +340,7 @@ p.parseD = function(msg) { var fields = []; for(var i = 0; i < fieldCount; i++) { var length = this.parseInt32(); - fields[i] = (length == -1 ? null : this.readString(length)) + fields[i] = (length === -1 ? null : this.readString(length)) }; msg.fieldCount = fieldCount; msg.fields = fields; diff --git a/test/unit/prepared-query-tests.js b/test/unit/prepared-query-tests.js index 195ccc93..092c0622 100644 --- a/test/unit/prepared-query-tests.js +++ b/test/unit/prepared-query-tests.js @@ -8,9 +8,31 @@ test('prepared queries', function() { test("parse messages", function() { - test('parsing a query with no parameters', function() { - client.parse('!'); + test('parsing a query with no parameters and no name', function() { + client.parse({text: '!'}); assert.length(client.stream.packets, 1); + var expected = new BufferList() + .addCString("") + .addCString("!") + .addInt16(0).join(true, 'P'); + assert.equalBuffers(client.stream.packets.pop(), expected); + client.stream.emit('data', buffers.readyForQuery()); + }); + + test('parsing a query with a name', function() { + //clear packets + client.parse({ + name: 'boom', + text: 'select * from boom', + types: [] + }); + assert.length(client.stream.packets, 1); + var expected = new BufferList() + .addCString("boom") + .addCString("select * from boom") + .addInt16(0).join(true,'P'); + assert.equalBuffers(client.stream.packets.pop(), expected); + client.stream.emit('data', buffers.readyForQuery()); }); }); diff --git a/test/unit/test-helper.js b/test/unit/test-helper.js index 7f263163..01908636 100644 --- a/test/unit/test-helper.js +++ b/test/unit/test-helper.js @@ -2,7 +2,7 @@ sys = require('sys'); assert = require('assert'); Client = require(__dirname+'/../../lib/client'); EventEmitter = require('events').EventEmitter; -BufferList = require(__dirname+'/buffer-list'); +BufferList = require(__dirname+'/../../lib/buffer-list'); buffers = require(__dirname+'/test-buffers'); assert.same = function(actual, expected) {