diff --git a/lib/connection.js b/lib/connection.js index d232d4f5..b4198bff 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -75,21 +75,12 @@ p.startup = function(config) { p.password = function(password) { //0x70 = 'p' - this.send(0x70, this.writer.addCString(password).flush()); + this._send(0x70, this.writer.addCString(password)); }; -p.send = function(code, bodyBuffer) { - var length = bodyBuffer.length + 4; - var buffer = Buffer(length + 1); - var offset = 0; - buffer[offset++] = code; - buffer[offset++] = length >>> 24 & 0xFF; - buffer[offset++] = length >>> 16 & 0xFF; - buffer[offset++] = length >>> 8 & 0xFF; - buffer[offset++] = length >>> 0 & 0xFF; - bodyBuffer.copy(buffer, offset, 0); - return this.stream.write(buffer); -}; +p._send = function(code, writer) { + return this.stream.write(writer.flush(code)); +} var termBuffer = new Buffer([0x58, 0, 0, 0, 4]); p.end = function() { @@ -98,7 +89,7 @@ p.end = function() { p.query = function(text) { //0x51 = Q - this.send(0x51, this.writer.addCString(text).flush()); + this.stream.write(this.writer.addCString(text).flush(0x51)); }; p.parse = function(query) { @@ -121,7 +112,7 @@ p.parse = function(query) { } //0x50 = 'P' - this.send(0x50, buffer.flush()); + this._send(0x50, buffer); return this; }; @@ -151,7 +142,8 @@ p.bind = function(config) { buffer.addInt16(1); // format codes to use binary buffer.addInt16(1); //0x42 = 'B' - this.send(0x42, buffer.flush()); + + this._send(0x42, buffer); }; p.execute = function(config) { @@ -160,30 +152,31 @@ p.execute = function(config) { config.rows = config.rows || ''; var buffer = this.writer .addCString(config.portal) - .addInt32(config.rows) - .flush(); - + .addInt32(config.rows); + //0x45 = 'E' - this.send(0x45, buffer); + this._send(0x45, buffer); }; +var emptyBuffer = Buffer(0); + p.flush = function() { //0x48 = 'H' - this.send(0x48,Buffer(0)); + this._send(0x48,this.writer.add(emptyBuffer)); } p.sync = function() { //0x53 = 'S' - this.send(0x53, Buffer(0)); + this._send(0x53, this.writer.add(emptyBuffer)); }; p.end = function() { //0x58 = 'X' - this.send(0x58, Buffer(0)); + this._send(0x58, this.writer.add(emptyBuffer)); }; p.describe = function(msg) { - this.send(0x44, this.writer.addCString(msg.type + (msg.name || '')).flush()); + this._send(0x44, this.writer.addCString(msg.type + (msg.name || ''))); }; //parsing methods diff --git a/lib/query.js b/lib/query.js index 592c6d44..2e4f6dd9 100644 --- a/lib/query.js +++ b/lib/query.js @@ -1,5 +1,6 @@ var EventEmitter = require('events').EventEmitter; var sys = require('sys');var sys = require('sys'); +var Result = require(__dirname + "/result"); var Query = function(config) { this.text = config.text; @@ -26,6 +27,53 @@ var noParse = function(val) { return val; }; +//creates datarow metatdata from the supplied +//data row information +var buildDataRowMetadata = function(msg, converters, names) { + var len = msg.fields.length; + for(var i = 0; i < len; i++) { + var field = msg.fields[i]; + var dataTypeId = field.dataTypeID; + names[i] = field.name; + switch(dataTypeId) { + case 20: + converters[i] = parseBinaryInt64; + break; + case 21: + converters[i] = parseBinaryInt16; + break; + case 23: + converters[i] = parseBinaryInt32; + break; + case 26: + converters[i] = parseBinaryInt64; + break; + case 1700: + case 700: + converters[i] = parseBinaryFloat32; + case 701: + converters[i] = parseBinaryFloat64; + break; + case 16: + converters[i] = function(val) { + return val == 1; + }; + break; + case 1114: + case 1184: + converters[i] = parseDate; + break; + case 1007: + case 1008: + converters[i] = arrayParser, + break; + default: + converters[i] = dataTypeParsers[dataTypeId] || noParse; + break; + } + }; +} + p.submit = function(connection) { var self = this; if(this.requiresPreparation()) { @@ -33,47 +81,38 @@ p.submit = function(connection) { } else { connection.query(this.text); } + var converters = []; var names = []; - var rows = []; var handleRowDescription = function(msg) { - for(var i = 0; i < msg.fields.length; i++) { - converters[i] = dataTypeParsers[msg.fields[i].dataTypeID] || noParse; - names[i] = msg.fields[i].name; - }; + buildDataRowMetadata(msg, converters, names); }; + + var result = new Result(); + var handleDatarow = function(msg) { - var result = {}; + var row = {}; for(var i = 0; i < msg.fields.length; i++) { var rawValue = msg.fields[i]; - result[names[i]] = rawValue === null ? null : converters[i](rawValue); + row[names[i]] = rawValue === null ? null : converters[i](rawValue); } - self.emit('row', result); + self.emit('row', row); - //if no reciever, buffer rows + //if there is a callback collect rows if(self.callback) { - rows.push(result); + result.addRow(row); } }; - var onCommandComplete = function() { - self.callback(null, {rows: rows}); - rows = []; - } - - if(self.callback) { - connection.on('commandComplete', onCommandComplete) - } + var onCommandComplete = function(msg) { + result.addCommandComplete(msg); + }; var onError = function(err) { //remove all listeners - connection.removeListener('rowDescription', handleDatarow); - connection.removeListener('dataRow', handleDatarow); - connection.removeListener('error', onError); - connection.removeListener('readyForQuery', onReadyForQuery); + removeListeners(); if(self.callback) { self.callback(err); - connection.removeListener('commandComplete', onCommandComplete); } else { self.emit('error', err); } @@ -81,21 +120,26 @@ p.submit = function(connection) { }; var onReadyForQuery = function() { + removeListeners(); + if(self.callback) { + self.callback(null, result); + } + self.emit('end', result); + }; + + var removeListeners = function() { //remove all listeners connection.removeListener('rowDescription', handleRowDescription); connection.removeListener('dataRow', handleDatarow); connection.removeListener('readyForQuery', onReadyForQuery); + connection.removeListener('commandComplete', onCommandComplete); connection.removeListener('error', onError); - if(self.callback) { - connection.removeListener('commandComplete', onCommandComplete); - } - self.emit('end'); }; connection.on('rowDescription', handleRowDescription); connection.on('dataRow', handleDatarow); connection.on('readyForQuery', onReadyForQuery); - + connection.on('commandComplete', onCommandComplete); connection.on('error', onError); }; @@ -107,12 +151,12 @@ p.prepare = function(connection) { var self = this; if(!this.hasBeenParsed(connection)) { - connection.parsedStatements[this.name] = true; connection.parse({ text: self.text, name: self.name, types: self.types }); + connection.parsedStatements[this.name] = true; } //TODO is there some btter way to prepare values for the database? @@ -144,7 +188,6 @@ p.prepare = function(connection) { getRows(); - //TODO support EmptyQueryResponse, ErrorResponse, and PortalSuspended var onCommandComplete = function() { connection.removeListener('error', onCommandComplete); connection.removeListener('commandComplete', onCommandComplete); @@ -159,32 +202,28 @@ p.prepare = function(connection) { }; var dateParser = function(isoDate) { - //TODO find some regexp help - //this method works but it's ooglay - //if you wanna contribute...... ;) - var split = isoDate.split(' '); - var dateMatcher = /(\d{4})-(\d{2})-(\d{2})/; + //TODO this could do w/ a refactor - var date = split[0]; - var time = split[1]; - var match = dateMatcher.exec(date); - var splitDate = date.split('-'); + var dateMatcher = /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?/; + + var match = dateMatcher.exec(isoDate); var year = match[1]; var month = parseInt(match[2],10)-1; var day = match[3]; + var hour = parseInt(match[4],10); + var min = parseInt(match[5],10); + var seconds = parseInt(match[6], 10); - var splitTime = time.split(':'); - var hour = parseInt(splitTime[0],10); - var min = parseInt(splitTime[1],10); - var end = splitTime[2]; - var seconds = /(\d{2})/.exec(end); - seconds = (seconds ? seconds[1] : 0); - seconds = parseInt(seconds,10); - var mili = /\.(\d{1,})/.exec(end+"000"); - mili = mili ? mili[1].slice(0,3) : 0; - var tZone = /([Z|+\-])(\d{2})?(\d{2})?/.exec(end); + var miliString = match[7]; + var mili = 0; + if(miliString) { + mili = 1000 * parseFloat(miliString); + } + + var tZone = /([Z|+\-])(\d{2})?(\d{2})?/.exec(isoDate.split(' ')[1]); //minutes to adjust for timezone var tzAdjust = 0; + if(tZone) { var type = tZone[1]; switch(type) { @@ -408,23 +447,6 @@ var arrayParser = function(value) { Query.dateParser = dateParser; var dataTypeParsers = { - 16: function(dbVal) { //boolean - console.log(JSON.stringify(dbVal)); - return value[0] == 1; - }, - - 20: parseBinaryInt64, - 21: parseBinaryInt16, - 23: parseBinaryInt32, - 26: parseBinaryInt64, - 700: parseBinaryFloat32, - 701: parseBinaryFloat64, - 1007: arrayParser, - 1009: arrayParser, - 1114: parseDate, - //1184: parseBinaryInt64 - // 1700: parseFloat, }; - module.exports = Query;