parsing of prepared queries with no parameters

This commit is contained in:
brianc 2010-10-23 14:50:28 -05:00
parent 380abbf6a6
commit 95ec1b403c
4 changed files with 73 additions and 22 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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());
});
});

View File

@ -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) {