diff --git a/Makefile b/Makefile index b4885d14c..0a3bd679a 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ node-command := xargs -n 1 -I file node file $(params) .PHONY : test test-connection test-integration bench test-native build/default/binding.node test: test-unit -test-all: test-unit test-integration test-native +test-all: test-unit test-integration test-native test-binary bench: @find benchmark -name "*-bench.js" | $(node-command) @@ -28,6 +28,9 @@ test-unit: test-connection: @node script/test-connection.js $(params) +test-connection-binary: + @node script/test-connection.js $(params) --binary true + test-native: build/default/binding.node @echo "***Testing native bindings***" @find test/native -name "*-tests.js" | $(node-command) @@ -36,3 +39,7 @@ test-native: build/default/binding.node test-integration: test-connection @echo "***Testing Pure Javascript***" @find test/integration -name "*-tests.js" | $(node-command) + +test-binary: test-connection-binary + @echo "***Testing Pure Javascript (binary)***" + @find test/integration -name "*-tests.js" | $(node-command) --binary true diff --git a/lib/binaryParsers.js b/lib/binaryParsers.js new file mode 100644 index 000000000..9333a972e --- /dev/null +++ b/lib/binaryParsers.js @@ -0,0 +1,258 @@ +var parseBits = function(data, bits, offset, invert, callback) { + offset = offset || 0; + invert = invert || false; + callback = callback || function(lastValue, newValue, bits) { return (lastValue * Math.pow(2, bits)) + newValue; }; + var offsetBytes = offset >> 3; + + var inv = function(value) { + if (invert) { + return ~value & 0xff; + } + + return value; + }; + + // read first (maybe partial) byte + var mask = 0xff; + var firstBits = 8 - (offset % 8); + if (bits < firstBits) { + mask = (0xff << (8 - bits)) & 0xff; + firstBits = bits; + } + + if (offset) { + mask = mask >> (offset % 8); + } + + var result = 0; + if ((offset % 8) + bits >= 8) { + result = callback(0, inv(data[offsetBytes]) & mask, firstBits); + } + + // read bytes + var bytes = (bits + offset) >> 3; + for (var i = offsetBytes + 1; i < bytes; i++) { + result = callback(result, inv(data[i]), 8); + } + + // bits to read, that are not a complete byte + var lastBits = (bits + offset) % 8; + if (lastBits > 0) { + result = callback(result, inv(data[bytes]) >> (8 - lastBits), lastBits); + } + + return result; +}; + +var parseFloatFromBits = function(data, precisionBits, exponentBits) { + var bias = Math.pow(2, exponentBits - 1) - 1; + var sign = parseBits(data, 1); + var exponent = parseBits(data, exponentBits, 1); + + if (exponent === 0) + return 0; + + // parse mantissa + var precisionBitsCounter = 1; + var parsePrecisionBits = function(lastValue, newValue, bits) { + if (lastValue === 0) { + lastValue = 1; + } + + for (var i = 1; i <= bits; i++) { + precisionBitsCounter /= 2; + if ((newValue & (0x1 << (bits - i))) > 0) { + lastValue += precisionBitsCounter; + } + } + + return lastValue; + }; + + var mantissa = parseBits(data, precisionBits, exponentBits + 1, false, parsePrecisionBits); + + // special cases + if (exponent == (Math.pow(2, exponentBits + 1) - 1)) { + if (mantissa === 0) { + return (sign === 0) ? Infinity : -Infinity; + } + + return NaN; + } + + // normale number + return ((sign === 0) ? 1 : -1) * Math.pow(2, exponent - bias) * mantissa; +}; + +var parseBool = function(value) { + return (parseBits(value, 8) == 1); +}; + +var parseInt16 = function(value) { + if (parseBits(value, 1) == 1) { + return -1 * (parseBits(value, 15, 1, true) + 1); + } + + return parseBits(value, 15, 1); +}; + +var parseInt32 = function(value) { + if (parseBits(value, 1) == 1) { + return -1 * (parseBits(value, 31, 1, true) + 1); + } + + return parseBits(value, 31, 1); +}; + +var parseInt64 = function(value) { + if (parseBits(value, 1) == 1) { + return -1 * (parseBits(value, 63, 1, true) + 1); + } + + return parseBits(value, 63, 1); +}; + +var parseFloat32 = function(value) { + return parseFloatFromBits(value, 23, 8); +}; + +var parseFloat64 = function(value) { + return parseFloatFromBits(value, 52, 11); +}; + +var parseNumeric = function(value) { + var sign = parseBits(value, 16, 32); + if (sign == 0xc000) { + return NaN; + } + + var weight = Math.pow(10000, parseBits(value, 16, 16)); + var result = 0; + + var digits = []; + var ndigits = parseBits(value, 16); + for (var i = 0; i < ndigits; i++) { + result += parseBits(value, 16, 64 + (16 * i)) * weight; + weight /= 10000; + } + + var scale = Math.pow(10, parseBits(value, 16, 48)); + return ((sign === 0) ? 1 : -1) * Math.round(result * scale) / scale; +}; + +var parseDate = function(value) { + var sign = parseBits(value, 1); + var rawValue = parseBits(value, 63, 1); + + // discard usecs and shift from 2000 to 1970 + var result = new Date((((sign === 0) ? 1 : -1) * rawValue / 1000) + 946684800000); + + // add microseconds to the date + result.usec = rawValue % 1000; + result.getMicroSeconds = function() { + return this.usec; + }; + result.setMicroSeconds = function(value) { + this.usec = value; + }; + result.getUTCMicroSeconds = function() { + return this.usec; + }; + + return result; +}; + +var parseArray = function(value) { + var dim = parseBits(value, 32); + + var flags = parseBits(value, 32, 32); + var elementType = parseBits(value, 32, 64); + + var offset = 96; + var dims = []; + for (var i = 0; i < dim; i++) { + // parse dimension + dims[i] = parseBits(value, 32, offset); + offset += 32; + + // ignore lower bounds + offset += 32; + } + + var parseElement = function(elementType) { + // parse content length + var length = parseBits(value, 32, offset); + offset += 32; + + // parse null values + if (length == 0xffffffff) { + return null; + } + + if ((elementType == 0x17) || (elementType == 0x14)) { + // int/bigint + var result = parseBits(value, length * 8, offset); + offset += length * 8; + return result; + } + else if (elementType == 0x19) { + // string + var result = value.toString(this.encoding, offset >> 3, (offset += (length << 3)) >> 3); + return result; + } + else { + console.log("ERROR: ElementType not implemented: " + elementType); + } + }; + + var parse = function(dimension, elementType) { + var array = []; + + if (dimension.length > 1) { + var count = dimension.shift(); + for (var i = 0; i < count; i++) { + array[i] = parse(dimension, elementType); + } + dimension.unshift(count); + } + else { + for (var i = 0; i < dimension[0]; i++) { + array[i] = parseElement(elementType); + } + } + + return array; + }; + + return parse(dims, elementType); +}; + +var parseText = function(value) { + return value.toString('utf8'); +}; + +var parseBool = function(value) { + return (parseBits(value, 8) > 0); +}; + +var init = function(register) { + register(20, parseInt64); + register(21, parseInt16); + register(23, parseInt32); + register(26, parseInt32); + register(1700, parseNumeric); + register(700, parseFloat32); + register(701, parseFloat64); + register(16, parseBool); + register(1114, parseDate); + register(1184, parseDate); + register(1007, parseArray); + register(1016, parseArray); + register(1008, parseArray); + register(1009, parseArray); + register(25, parseText); +}; + +module.exports = { + init: init +}; diff --git a/lib/client.js b/lib/client.js index 891e18120..5d1af39aa 100644 --- a/lib/client.js +++ b/lib/client.js @@ -20,6 +20,7 @@ var Client = function(config) { this.connection = config.connection || new Connection({stream: config.stream}); this.queryQueue = []; this.password = config.password || defaults.password; + this.binary = config.binary || defaults.binary; this.encoding = 'utf8'; this.processID = null; this.secretKey = null; @@ -172,7 +173,10 @@ p._pulseQueryQueue = function() { p.query = function(config, values, callback) { //can take in strings or config objects - config = (config.text || config.name) ? config : { text: config }; + config = (typeof(config) == 'string') ? { text: config } : config; + if (this.binary && !('binary' in config)) { + config.binary = true; + } if(values) { if(typeof values === 'function') { diff --git a/lib/connection.js b/lib/connection.js index c70dd0d89..6d9f11fb3 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -140,6 +140,7 @@ p.bind = function(config, more) { config = config || {}; config.portal = config.portal || ''; config.statement = config.statement || ''; + config.binary = config.binary || false; var values = config.values || []; var len = values.length; var buffer = this.writer @@ -157,7 +158,14 @@ p.bind = function(config, more) { buffer.addString(val); } } - buffer.addInt16(0); //no format codes, use text + + if (config.binary) { + buffer.addInt16(1); // format codes to use binary + buffer.addInt16(1); + } + else { + buffer.addInt16(0); // format codes to use text + } //0x42 = 'B' this._send(0x42, more); }; @@ -383,7 +391,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.readBytes(length)) }; msg.fieldCount = fieldCount; msg.fields = fields; @@ -466,6 +474,10 @@ p.readString = function(length) { return this.buffer.toString(this.encoding, this.offset, (this.offset += length)); }; +p.readBytes = function(length) { + return this.buffer.slice(this.offset, this.offset += length); +}; + p.parseCString = function() { var start = this.offset; while(this.buffer[this.offset++]) { }; diff --git a/lib/defaults.js b/lib/defaults.js index e60c74379..8df164cab 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -14,5 +14,7 @@ module.exports = { //0 will disable connection pooling poolSize: 10, //duration of node-pool timeout - poolIdleTimeout: 30000 + poolIdleTimeout: 30000, + // binary result mode + binary: false } diff --git a/lib/native/query.js b/lib/native/query.js index dbfd2b2d1..6187c20a5 100644 --- a/lib/native/query.js +++ b/lib/native/query.js @@ -60,7 +60,7 @@ var mapRowData = function(row) { var result = {}; for(var i = 0, len = row.length; i < len; i++) { var item = row[i]; - result[item.name] = item.value == null ? null : types.getStringTypeParser(item.type)(item.value); + result[item.name] = item.value == null ? null : types.getTypeParser(item.type, 'text')(item.value); } return result; } diff --git a/lib/query.js b/lib/query.js index 176c732ae..3d5dfc08f 100644 --- a/lib/query.js +++ b/lib/query.js @@ -2,7 +2,7 @@ var EventEmitter = require('events').EventEmitter; var util = require('util'); var Result = require(__dirname + "/result"); -var types = require(__dirname + "/types"); +var Types = require(__dirname + "/types"); var Query = function(config) { this.text = config.text; @@ -10,6 +10,7 @@ var Query = function(config) { this.rows = config.rows; this.types = config.types; this.name = config.name; + this.binary = config.binary; //use unique portal name each time this.portal = config.portal || "" this.callback = config.callback; @@ -24,7 +25,7 @@ util.inherits(Query, EventEmitter); var p = Query.prototype; p.requiresPreparation = function() { - return (this.values || 0).length > 0 || this.name || this.rows; + return (this.values || 0).length > 0 || this.name || this.rows || this.binary; }; @@ -41,8 +42,9 @@ p.handleRowDescription = function(msg) { var len = msg.fields.length; for(var i = 0; i < len; i++) { var field = msg.fields[i]; + var format = field.format; this._fieldNames[i] = field.name; - this._fieldConverters[i] = types.getStringTypeParser(field.dataTypeID); + this._fieldConverters[i] = Types.getTypeParser(field.dataTypeID, format); }; }; @@ -136,7 +138,8 @@ p.prepare = function(connection) { connection.bind({ portal: self.portalName, statement: self.name, - values: self.values + values: self.values, + binary: self.binary }, true); connection.describe({ diff --git a/lib/textParsers.js b/lib/textParsers.js new file mode 100644 index 000000000..8db5e522e --- /dev/null +++ b/lib/textParsers.js @@ -0,0 +1,130 @@ +//parses PostgreSQL server formatted date strings into javascript date objects +var parseDate = function(isoDate) { + //TODO this could do w/ a refactor + var dateMatcher = /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?/; + + var match = dateMatcher.exec(isoDate); + //could not parse date + if(!match) { + return null; + } + 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 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) { + case 'Z': break; + case '-': + tzAdjust = -(((parseInt(tZone[2],10)*60)+(parseInt(tZone[3]||0,10)))); + break; + case '+': + tzAdjust = (((parseInt(tZone[2],10)*60)+(parseInt(tZone[3]||0,10)))); + break; + default: + throw new Error("Unidentifed tZone part " + type); + } + } + + var utcOffset = Date.UTC(year, month, day, hour, min, seconds, mili); + + var date = new Date(utcOffset - (tzAdjust * 60* 1000)); + return date; +}; + +var parseBool = function(val) { + return val === 't'; +} + +var parseIntegerArray = function(val) { + return JSON.parse(val.replace("{","[").replace("}","]")); +}; + +var parseStringArray = function(val) { + if (!val) return null; + if (val[0] !== '{' || val[val.length-1] !== '}') + throw "Not postgresql array! (" + arrStr + ")"; + + var x = val.substring(1, val.length - 1); + x = x.match(/(NULL|[^,]+|"((?:.|\n|\r)*?)(?!\\)"|\{((?:.|\n|\r)*?(?!\\)\}) (,|$))/mg); + if (x === null) throw "Not postgre array"; + return x.map(function (el) { + if (el === 'NULL') return null; + if (el[0] === '{') return arguments.callee(el); + if (el[0] === '\"') return el.substring(1, el.length - 1).replace('\\\"', '\"'); + return el; + }); +}; + + +var NUM = '([+-]?\\d+)'; +var YEAR = NUM + '\\s+years?'; +var MON = NUM + '\\s+mons?'; +var DAY = NUM + '\\s+days?'; +var TIME = '([+-])?(\\d\\d):(\\d\\d):(\\d\\d)'; +var INTERVAL = [YEAR,MON,DAY,TIME].map(function(p){ return "("+p+")?" }).join('\\s*'); + +var parseInterval = function(val) { + if (!val) return {}; + var m = new RegExp(INTERVAL).exec(val); + var i = {}; + if (m[2]) i.years = parseInt(m[2]); + if (m[4]) i.months = parseInt(m[4]); + if (m[6]) i.days = parseInt(m[6]); + if (m[9]) i.hours = parseInt(m[9]); + if (m[10]) i.minutes = parseInt(m[10]); + if (m[11]) i.seconds = parseInt(m[11]); + if (m[8] == '-'){ + if (i.hours) i.hours *= -1; + if (i.minutes) i.minutes *= -1; + if (i.seconds) i.seconds *= -1; + } + for (field in i){ + if (i[field] == 0) + delete i[field]; + } + return i; +}; + +var parseByteA = function(val) { + return new Buffer(val.replace(/\\([0-7]{3})/g, function (full_match, code) { + return String.fromCharCode(parseInt(code, 8)); + }).replace(/\\\\/g, "\\"), "binary"); +} + +var init = function(register) { + register(20, parseInt); + register(21, parseInt); + register(23, parseInt); + register(26, parseInt); + register(1700, parseFloat); + register(700, parseFloat); + register(701, parseFloat); + register(16, parseBool); + register(1114, parseDate); + register(1184, parseDate); + register(1007, parseIntegerArray); + register(1016, parseIntegerArray); + register(1008, parseStringArray); + register(1009, parseStringArray); + register(1186, parseInterval); + register(17, parseByteA); +}; + +module.exports = { + init: init, +}; diff --git a/lib/types.js b/lib/types.js index 0637f09a9..c2bbc1108 100644 --- a/lib/types.js +++ b/lib/types.js @@ -1,150 +1,35 @@ -//maps types from javascript to postgres and vise-versa +var textParsers = require(__dirname + "/textParsers"), +binaryParsers = require(__dirname + "/binaryParsers"); -//the empty parse function -var noParse = function(val) { - return val; -} - -//parses PostgreSQL server formatted date strings into javascript date objects -var parseDate = function(isoDate) { - //TODO this could do w/ a refactor - var dateMatcher = /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?/; - - var match = dateMatcher.exec(isoDate); - //could not parse date - if(!match) { - return null; - } - 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 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) { - case 'Z': break; - case '-': - tzAdjust = -(((parseInt(tZone[2],10)*60)+(parseInt(tZone[3]||0,10)))); - break; - case '+': - tzAdjust = (((parseInt(tZone[2],10)*60)+(parseInt(tZone[3]||0,10)))); - break; - default: - throw new Error("Unidentifed tZone part " + type); - } - } - - var utcOffset = Date.UTC(year, month, day, hour, min, seconds, mili); - - var date = new Date(utcOffset - (tzAdjust * 60* 1000)); - return date; -}; - -var parseBool = function(val) { - return val === 't'; -} - -var parseIntegerArray = function(val) { - return JSON.parse(val.replace("{","[").replace("}","]")); +var typeParsers = { + text: {}, + binary: {} }; -var parseStringArray = function(val) { - if (!val) return null; - if (val === '{}') return []; - if (val[0] !== '{' || val[val.length-1] !== '}') - throw "Not postgresql array! (" + arrStr + ")"; - - var x = val.substring(1, val.length - 1); - x = x.match(/(NULL|[^,]+|"((?:.|\n|\r)*?)(?!\\)"|\{((?:.|\n|\r)*?(?!\\)\}) (,|$))/mg); - if (x === null) throw "Not postgre array"; - return x.map(function (el) { - if (el === 'NULL') return null; - if (el[0] === '{') return arguments.callee(el); - if (el[0] === '\"') return el.substring(1, el.length - 1).replace('\\\"', '\"'); - return el; - }); -}; - - -var NUM = '([+-]?\\d+)'; -var YEAR = NUM + '\\s+years?'; -var MON = NUM + '\\s+mons?'; -var DAY = NUM + '\\s+days?'; -var TIME = '([+-])?(\\d\\d):(\\d\\d):(\\d\\d)'; -var INTERVAL = [YEAR,MON,DAY,TIME].map(function(p){ return "("+p+")?" }).join('\\s*'); - - -var parseInterval = function(val) { - if (!val) return {}; - var m = new RegExp(INTERVAL).exec(val); - var i = {}; - if (m[2]) i.years = parseInt(m[2]); - if (m[4]) i.months = parseInt(m[4]); - if (m[6]) i.days = parseInt(m[6]); - if (m[9]) i.hours = parseInt(m[9]); - if (m[10]) i.minutes = parseInt(m[10]); - if (m[11]) i.seconds = parseInt(m[11]); - if (m[8] == '-'){ - if (i.hours) i.hours *= -1; - if (i.minutes) i.minutes *= -1; - if (i.seconds) i.seconds *= -1; - } - for (field in i){ - if (i[field] == 0) - delete i[field]; - } - return i; -}; - -var parseByteA = function(val) { - return new Buffer(val.replace(/\\([0-7]{3})/g, function (full_match, code) { - return String.fromCharCode(parseInt(code, 8)); - }).replace(/\\\\/g, "\\"), "binary"); +//the empty parse function +var noParse = function(val) { + return String(val); } -var typeParsers = {}; -//registers a method used to parse a string representing a particular -//oid type into a javascript type -var registerStringTypeParser = function(oid, converter) { - typeParsers[oid] = converter; -}; - //returns a function used to convert a specific type (specified by //oid) into a result javascript type -var getStringTypeParser = function(oid) { - return typeParsers[oid] || noParse; +var getTypeParser = function(oid, format) { + if (!typeParsers[format]) + return noParse; + + return typeParsers[format][oid] || noParse; }; -//default string type parser registrations -registerStringTypeParser(20, parseInt); -registerStringTypeParser(21, parseInt); -registerStringTypeParser(23, parseInt); -registerStringTypeParser(26, parseInt); -registerStringTypeParser(1700, parseFloat); -registerStringTypeParser(700, parseFloat); -registerStringTypeParser(701, parseFloat); -registerStringTypeParser(16, parseBool); -registerStringTypeParser(1114, parseDate); -registerStringTypeParser(1184, parseDate); -registerStringTypeParser(1007, parseIntegerArray); -registerStringTypeParser(1009, parseStringArray); -registerStringTypeParser(1186, parseInterval); -registerStringTypeParser(17, parseByteA); +textParsers.init(function(oid, converter) { + typeParsers.text[oid] = function(value) { + return converter(String(value)); + }; +}); + +binaryParsers.init(function(oid, converter) { + typeParsers.binary[oid] = converter; +}); module.exports = { - registerStringTypeParser: registerStringTypeParser, - getStringTypeParser: getStringTypeParser, + getTypeParser: getTypeParser, } diff --git a/script/test-connection.js b/script/test-connection.js index 3099a4dc3..811286104 100644 --- a/script/test-connection.js +++ b/script/test-connection.js @@ -1,9 +1,9 @@ var helper = require(__dirname + '/../test/test-helper'); -var connectionString = helper.connectionString(); + console.log(); -console.log("testing ability to connect to '%s'", connectionString); +console.log("testing ability to connect to '%j'", helper.config); var pg = require(__dirname + '/../lib'); -pg.connect(connectionString, function(err, client) { +pg.connect(helper.config, function(err, client) { if(err !== null) { console.error("Recieved connection error when attempting to contact PostgreSQL:"); console.error(err); diff --git a/test/cli.js b/test/cli.js index b781f5b6d..04f583215 100644 --- a/test/cli.js +++ b/test/cli.js @@ -40,6 +40,8 @@ for(var i = 0; i < args.length; i++) { config.test = args[++i]; case '--native': config.native = (args[++i] == "true"); + case '--binary': + config.binary = (args[++i] == "true"); default: break; } diff --git a/test/integration/client/api-tests.js b/test/integration/client/api-tests.js index 023cc297b..0fd941089 100644 --- a/test/integration/client/api-tests.js +++ b/test/integration/client/api-tests.js @@ -5,20 +5,18 @@ if(helper.args.native) { pg = require(__dirname + '/../../../lib').native; } -var connectionString = helper.connectionString(__filename); - var log = function() { //console.log.apply(console, arguments); } var sink = new helper.Sink(5, 10000, function() { - log("ending connection pool: %s", connectionString); - pg.end(connectionString); + log("ending connection pool: %j", helper.config); + pg.end(helper.config); }); test('api', function() { - log("connecting to %s", connectionString) - pg.connect(connectionString, assert.calls(function(err, client) { + log("connecting to %j", helper.config) + pg.connect(helper.config, assert.calls(function(err, client) { assert.equal(err, null, "Failed to connect: " + helper.sys.inspect(err)); client.query('CREATE TEMP TABLE band(name varchar(100))'); @@ -60,7 +58,7 @@ test('api', function() { }) test('executing nested queries', function() { - pg.connect(connectionString, assert.calls(function(err, client) { + pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); log("connected for nested queriese") client.query('select now as now from NOW()', assert.calls(function(err, result) { @@ -87,7 +85,7 @@ test('raises error if cannot connect', function() { }) test("query errors are handled and do not bubble if callback is provded", function() { - pg.connect(connectionString, assert.calls(function(err, client) { + pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err) log("checking for query error") client.query("SELECT OISDJF FROM LEIWLISEJLSE", assert.calls(function(err, result) { @@ -99,7 +97,7 @@ test("query errors are handled and do not bubble if callback is provded", functi }) test('callback is fired once and only once', function() { - pg.connect(connectionString, assert.calls(function(err, client) { + pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query("CREATE TEMP TABLE boom(name varchar(10))"); var callCount = 0; @@ -115,7 +113,7 @@ test('callback is fired once and only once', function() { }) test('can provide callback and config object', function() { - pg.connect(connectionString, assert.calls(function(err, client) { + pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query({ name: 'boom', @@ -128,7 +126,7 @@ test('can provide callback and config object', function() { }) test('can provide callback and config and parameters', function() { - pg.connect(connectionString, assert.calls(function(err, client) { + pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); var config = { text: 'select $1::text as val' @@ -142,7 +140,7 @@ test('can provide callback and config and parameters', function() { }) test('null and undefined are both inserted as NULL', function() { - pg.connect(connectionString, assert.calls(function(err, client) { + pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query("CREATE TEMP TABLE my_nulls(a varchar(1), b varchar(1), c integer, d integer, e date, f date)"); client.query("INSERT INTO my_nulls(a,b,c,d,e,f) VALUES ($1,$2,$3,$4,$5,$6)", [ null, undefined, null, undefined, null, undefined ]); diff --git a/test/integration/client/array-tests.js b/test/integration/client/array-tests.js index 669100c83..548b37680 100644 --- a/test/integration/client/array-tests.js +++ b/test/integration/client/array-tests.js @@ -1,9 +1,8 @@ var helper = require(__dirname + "/test-helper"); var pg = helper.pg; -var conString = helper.connectionString(); test('parsing array results', function() { - pg.connect(conString, assert.calls(function(err, client) { + pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query("CREATE TEMP TABLE why(names text[], numbors integer[])"); client.query('INSERT INTO why(names, numbors) VALUES(\'{"aaron", "brian","a b c" }\', \'{1, 2, 3}\')').on('error', console.log); diff --git a/test/integration/client/cancel-query-tests.js b/test/integration/client/cancel-query-tests.js index 36d5710b6..842b471ae 100644 --- a/test/integration/client/cancel-query-tests.js +++ b/test/integration/client/cancel-query-tests.js @@ -28,9 +28,9 @@ test("cancellation of a query", function() { rows4++; }); - helper.pg.cancel(helper.connectionString, client, query1); - helper.pg.cancel(helper.connectionString, client, query2); - helper.pg.cancel(helper.connectionString, client, query4); + helper.pg.cancel(helper.config, client, query1); + helper.pg.cancel(helper.config, client, query2); + helper.pg.cancel(helper.config, client, query4); setTimeout(function() { assert.equal(rows1, 0); diff --git a/test/integration/client/drain-tests.js b/test/integration/client/drain-tests.js index 0aff28eb4..b6a2434d4 100644 --- a/test/integration/client/drain-tests.js +++ b/test/integration/client/drain-tests.js @@ -6,7 +6,7 @@ if(helper.args.native) { } var testDrainOfClientWithPendingQueries = function() { - pg.connect(helper.connectionString(), assert.success(function(client) { + pg.connect(helper.config, assert.success(function(client) { test('when there are pending queries and client is resumed', function() { var drainCount = 0; client.on('drain', function() { @@ -28,7 +28,7 @@ var testDrainOfClientWithPendingQueries = function() { })); }; -pg.connect(helper.connectionString(), assert.success(function(client) { +pg.connect(helper.config, assert.success(function(client) { var drainCount = 0; client.on('drain', function() { drainCount++; diff --git a/test/integration/client/empty-query-tests.js b/test/integration/client/empty-query-tests.js index 475acf793..3eb207c4a 100644 --- a/test/integration/client/empty-query-tests.js +++ b/test/integration/client/empty-query-tests.js @@ -5,11 +5,11 @@ test("empty query message handling", function() { assert.emits(client, 'drain', function() { client.end(); }); - client.query(""); + client.query({text: "", binary: false}); }); test('callback supported', assert.calls(function() { - client.query("", function(err, result) { + client.query({text: "", binary: false}, function(err, result) { assert.isNull(err); assert.empty(result.rows); }) diff --git a/test/integration/client/error-handling-tests.js b/test/integration/client/error-handling-tests.js index 4f1cb0cfe..0a855238f 100644 --- a/test/integration/client/error-handling-tests.js +++ b/test/integration/client/error-handling-tests.js @@ -30,7 +30,7 @@ test('error handling', function(){ var client = createErorrClient(); - var q = client.query("CREATE TEMP TABLE boom(age integer); INSERT INTO boom (age) VALUES (28);"); + var q = client.query({text: "CREATE TEMP TABLE boom(age integer); INSERT INTO boom (age) VALUES (28);", binary: false}); test("when query is parsing", function() { diff --git a/test/integration/client/result-metadata-tests.js b/test/integration/client/result-metadata-tests.js index b69028f60..1c4f94df1 100644 --- a/test/integration/client/result-metadata-tests.js +++ b/test/integration/client/result-metadata-tests.js @@ -1,10 +1,9 @@ var helper = require(__dirname + "/test-helper"); var pg = helper.pg; -var conString = helper.connectionString(); test('should return insert metadata', function() { return false; - pg.connect(conString, assert.calls(function(err, client) { + pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query("CREATE TEMP TABLE zugzug(name varchar(10))", assert.calls(function(err, result) { assert.isNull(err); diff --git a/test/integration/client/simple-query-tests.js b/test/integration/client/simple-query-tests.js index da573d43e..2edabd73f 100644 --- a/test/integration/client/simple-query-tests.js +++ b/test/integration/client/simple-query-tests.js @@ -37,7 +37,7 @@ test("simple query interface", function() { test("multiple simple queries", function() { var client = helper.client(); - client.query("create temp table bang(id serial, name varchar(5));insert into bang(name) VALUES('boom');") + client.query({ text: "create temp table bang(id serial, name varchar(5));insert into bang(name) VALUES('boom');", binary: false }) client.query("insert into bang(name) VALUES ('yes');"); var query = client.query("select name from bang"); assert.emits(query, 'row', function(row) { @@ -51,9 +51,9 @@ test("multiple simple queries", function() { test("multiple select statements", function() { var client = helper.client(); - client.query("create temp table boom(age integer); insert into boom(age) values(1); insert into boom(age) values(2); insert into boom(age) values(3)"); - client.query("create temp table bang(name varchar(5)); insert into bang(name) values('zoom');"); - var result = client.query("select age from boom where age < 2; select name from bang"); + client.query({text: "create temp table boom(age integer); insert into boom(age) values(1); insert into boom(age) values(2); insert into boom(age) values(3)", binary: false}); + client.query({text: "create temp table bang(name varchar(5)); insert into bang(name) values('zoom');", binary: false}); + var result = client.query({text: "select age from boom where age < 2; select name from bang", binary: false}); assert.emits(result, 'row', function(row) { assert.strictEqual(row['age'], 1); assert.emits(result, 'row', function(row) { diff --git a/test/integration/client/test-helper.js b/test/integration/client/test-helper.js index b01235c97..d8ae3d854 100644 --- a/test/integration/client/test-helper.js +++ b/test/integration/client/test-helper.js @@ -1,21 +1,10 @@ var helper = require(__dirname+'/../test-helper'); -module.exports = { - //creates a client from cli parameters - client: function() { - var client = new Client({ - database: helper.args.database, - user: helper.args.user, - password: helper.args.password, - host: helper.args.host, - port: helper.args.port - }); - - client.connect(); - return client; - }, - connectionString: helper.connectionString, - Sink: helper.Sink, - pg: helper.pg, - args: helper.args +//creates a client from cli parameters +helper.client = function() { + var client = new Client(helper.config); + client.connect(); + return client; }; + +module.exports = helper; diff --git a/test/integration/client/transaction-tests.js b/test/integration/client/transaction-tests.js index 8e3f8ac98..4fbfd18b9 100644 --- a/test/integration/client/transaction-tests.js +++ b/test/integration/client/transaction-tests.js @@ -5,9 +5,7 @@ var sink = new helper.Sink(2, function() { }); test('a single connection transaction', function() { - var connectionString = helper.connectionString(); - - helper.pg.connect(connectionString, assert.calls(function(err, client) { + helper.pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query('begin'); @@ -48,8 +46,7 @@ test('a single connection transaction', function() { }) test('gh#36', function() { - var connectionString = helper.connectionString(); - helper.pg.connect(connectionString, function(err, client) { + helper.pg.connect(helper.config, function(err, client) { if(err) throw err; client.query("BEGIN"); client.query({ diff --git a/test/integration/client/type-coercion-tests.js b/test/integration/client/type-coercion-tests.js index 124f82ef1..2c23f130c 100644 --- a/test/integration/client/type-coercion-tests.js +++ b/test/integration/client/type-coercion-tests.js @@ -1,9 +1,9 @@ var helper = require(__dirname + '/test-helper'); var sink; -var connectionString = helper.connectionString(); + var testForTypeCoercion = function(type){ - helper.pg.connect(connectionString, function(err, client) { - assert.isNull(err) + helper.pg.connect(helper.config, function(err, client) { + assert.isNull(err); client.query("create temp table test_type(col " + type.name + ")", assert.calls(function(err, result) { assert.isNull(err); test("Coerces " + type.name, function() { @@ -23,7 +23,7 @@ var testForTypeCoercion = function(type){ }); assert.emits(query, 'row', function(row) { - assert.strictEqual(row.col, val, "expected " + type.name + " of " + val + " but got " + row[0]); + assert.strictEqual(row.col, val, "expected " + type.name + " of " + val + " but got " + row.col); }, "row should have been called for " + type.name + " of " + val); client.query('delete from test_type'); @@ -79,6 +79,13 @@ var types = [{ values: ['13:12:12.321', null] }]; +// ignore some tests in binary mode +if (helper.config.binary) { + types = types.filter(function(type) { + return !(type.name in {'real':1, 'timetz':1, 'time':1}); + }); +} + var valueCount = 0; types.forEach(function(type) { valueCount += type.values.length; @@ -126,7 +133,7 @@ test("timestampz round trip", function() { client.on('drain', client.end.bind(client)); }); -helper.pg.connect(helper.connectionString(), assert.calls(function(err, client) { +helper.pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query('select null as res;', assert.calls(function(err, res) { assert.isNull(err); diff --git a/test/integration/connection-pool/ending-pool-tests.js b/test/integration/connection-pool/ending-pool-tests.js index c26904211..e46c0fc1b 100644 --- a/test/integration/connection-pool/ending-pool-tests.js +++ b/test/integration/connection-pool/ending-pool-tests.js @@ -1,8 +1,4 @@ var helper = require(__dirname + '/test-helper') -var conString1 = helper.connectionString(); -var conString2 = helper.connectionString(); -var conString3 = helper.connectionString(); -var conString4 = helper.connectionString(); var called = false; test('disconnects', function() { @@ -11,8 +7,8 @@ test('disconnects', function() { //this should exit the process, killing each connection pool helper.pg.end(); }); - [conString1, conString2, conString3, conString4].forEach(function() { - helper.pg.connect(conString1, function(err, client) { + [helper.config, helper.config, helper.config, helper.config].forEach(function(config) { + helper.pg.connect(config, function(err, client) { assert.isNull(err); client.query("SELECT * FROM NOW()", function(err, result) { process.nextTick(function() { diff --git a/test/integration/connection-pool/error-tests.js b/test/integration/connection-pool/error-tests.js index c4653ef7e..11badf04a 100644 --- a/test/integration/connection-pool/error-tests.js +++ b/test/integration/connection-pool/error-tests.js @@ -2,24 +2,22 @@ var helper = require(__dirname + "/../test-helper"); var pg = require(__dirname + "/../../../lib"); helper.pg = pg; -var conString = helper.connectionString(); - //first make pool hold 2 clients -pg.defaults.poolSize = 2; +helper.pg.defaults.poolSize = 2; var killIdleQuery = 'SELECT procpid, (SELECT pg_terminate_backend(procpid)) AS killed FROM pg_stat_activity WHERE current_query LIKE \'\''; //get first client -pg.connect(conString, assert.success(function(client) { +helper.pg.connect(helper.config, assert.success(function(client) { client.id = 1; - pg.connect(conString, assert.success(function(client2) { + helper.pg.connect(helper.config, assert.success(function(client2) { client2.id = 2; //subscribe to the pg error event - assert.emits(pg, 'error', function(error, brokenClient) { + assert.emits(helper.pg, 'error', function(error, brokenClient) { assert.ok(error); assert.ok(brokenClient); assert.equal(client.id, brokenClient.id); - pg.end(); + helper.pg.end(); }); //kill the connection from client client2.query(killIdleQuery, assert.success(function(res) { diff --git a/test/integration/connection-pool/idle-timeout-tests.js b/test/integration/connection-pool/idle-timeout-tests.js index 342442e28..c6cbbd9f6 100644 --- a/test/integration/connection-pool/idle-timeout-tests.js +++ b/test/integration/connection-pool/idle-timeout-tests.js @@ -3,7 +3,7 @@ var helper = require(__dirname + '/test-helper'); helper.pg.defaults.poolIdleTimeout = 200; test('idle timeout', function() { - helper.pg.connect(helper.connectionString(), assert.calls(function(err, client) { + helper.pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.query('SELECT NOW()'); //just let this one time out diff --git a/test/integration/connection-pool/test-helper.js b/test/integration/connection-pool/test-helper.js index 8345ea1c0..cc86677d7 100644 --- a/test/integration/connection-pool/test-helper.js +++ b/test/integration/connection-pool/test-helper.js @@ -1,18 +1,15 @@ var helper = require(__dirname + "/../test-helper"); -var pg = require(__dirname + "/../../../lib"); -helper.pg = pg; -var testPoolSize = function(max) { - var conString = helper.connectionString(); +helper.testPoolSize = function(max) { var sink = new helper.Sink(max, function() { - helper.pg.end(conString); + helper.pg.end(); }); test("can pool " + max + " times", function() { for(var i = 0; i < max; i++) { helper.pg.poolSize = 10; test("connection #" + i + " executes", function() { - helper.pg.connect(conString, function(err, client) { + helper.pg.connect(helper.config, function(err, client) { assert.isNull(err); client.query("select * from person", function(err, result) { assert.lengthIs(result.rows, 26) @@ -30,11 +27,5 @@ var testPoolSize = function(max) { }) } -module.exports = { - args: helper.args, - pg: helper.pg, - connectionString: helper.connectionString, - Sink: helper.Sink, - testPoolSize: testPoolSize -} +module.exports = helper; diff --git a/test/integration/connection-pool/unique-name-tests.js b/test/integration/connection-pool/unique-name-tests.js index 84dda439d..a92a00414 100644 --- a/test/integration/connection-pool/unique-name-tests.js +++ b/test/integration/connection-pool/unique-name-tests.js @@ -6,36 +6,27 @@ helper.pg.defaults.password = helper.args.password; helper.pg.defaults.database = helper.args.database; helper.pg.defaults.port = helper.args.port; helper.pg.defaults.host = helper.args.host; +helper.pg.defaults.binary = helper.args.binary; helper.pg.defaults.poolIdleTimeout = 100; -var args = { - user: helper.args.user, - password: helper.args.password, - database: helper.args.database, - port: helper.args.port, - host: helper.args.host -} -var moreArgs = { - database: helper.args.database, - password: helper.args.password, - port: helper.args.port, - user: helper.args.user, - host: helper.args.host, - zomg: true +var moreArgs = {}; +for (c in helper.config) { + moreArgs[c] = helper.config[c]; } +moreArgs.zomg = true; -var badArgs = { - user: helper.args.user + 'laksdjfl', - host: helper.args.host, - password: helper.args.password + 'asldkfjlas', - database: helper.args.database, - port: helper.args.port, - zomg: true +var badArgs = {}; +for (c in helper.config) { + badArgs[c] = helper.config[c]; } +badArgs.user = badArgs.user + 'laksdjfl'; +badArgs.password = badArgs.password + 'asldkfjlas'; +badArgs.zomg = true; + test('connecting with complete config', function() { - helper.pg.connect(args, assert.calls(function(err, client) { + helper.pg.connect(helper.config, assert.calls(function(err, client) { assert.isNull(err); client.iGotAccessed = true; client.query("SELECT NOW()") diff --git a/test/integration/connection/query-tests.js b/test/integration/connection/query-tests.js index 79e0e13ad..f308546d7 100644 --- a/test/integration/connection/query-tests.js +++ b/test/integration/connection/query-tests.js @@ -20,6 +20,6 @@ test('simple query', function() { process.on('exit', function() { assert.equal(rows.length, 2); assert.equal(rows[0].length, 1); - assert.strictEqual(rows[0] [0], '1'); - assert.strictEqual(rows[1] [0], '2'); + assert.strictEqual(String(rows[0] [0]), '1'); + assert.strictEqual(String(rows[1] [0]), '2'); }); diff --git a/test/native/callback-api-tests.js b/test/native/callback-api-tests.js index 56b232697..450066822 100644 --- a/test/native/callback-api-tests.js +++ b/test/native/callback-api-tests.js @@ -1,9 +1,8 @@ var helper = require(__dirname + "/../test-helper"); var Client = require(__dirname + "/../../lib/native"); -var conString = helper.connectionString(); test('fires callback with results', function() { - var client = new Client(conString); + var client = new Client(helper.config); client.connect(); client.query('SELECT 1 as num', assert.calls(function(err, result) { assert.isNull(err); diff --git a/test/native/connection-tests.js b/test/native/connection-tests.js index e1641a4ff..1cb0ed88e 100644 --- a/test/native/connection-tests.js +++ b/test/native/connection-tests.js @@ -12,7 +12,7 @@ test('connecting with wrong parameters', function() { }); test('connects', function() { - var con = new Client(helper.connectionString()); + var con = new Client(helper.config); con.connect(); assert.emits(con, 'connect', function() { test('disconnects', function() { diff --git a/test/native/error-tests.js b/test/native/error-tests.js index 9d196e6f9..3184df57b 100644 --- a/test/native/error-tests.js +++ b/test/native/error-tests.js @@ -1,9 +1,8 @@ var helper = require(__dirname + "/../test-helper"); var Client = require(__dirname + "/../../lib/native"); -var conString = helper.connectionString(); test('query with non-text as first parameter throws error', function() { - var client = new Client(conString); + var client = new Client(helper.config); client.connect(); assert.emits(client, 'connect', function() { assert.throws(function() { @@ -14,7 +13,7 @@ test('query with non-text as first parameter throws error', function() { }) test('parameterized query with non-text as first parameter throws error', function() { - var client = new Client(conString); + var client = new Client(helper.config); client.connect(); assert.emits(client, 'connect', function() { assert.throws(function() { @@ -28,7 +27,7 @@ test('parameterized query with non-text as first parameter throws error', functi }) var connect = function(callback) { - var client = new Client(conString); + var client = new Client(helper.config); client.connect(); assert.emits(client, 'connect', function() { callback(client); diff --git a/test/native/evented-api-tests.js b/test/native/evented-api-tests.js index d70c824fe..db93f5bff 100644 --- a/test/native/evented-api-tests.js +++ b/test/native/evented-api-tests.js @@ -1,9 +1,8 @@ var helper = require(__dirname + "/../test-helper"); var Client = require(__dirname + "/../../lib/native"); -var conString = helper.connectionString(); var setupClient = function() { - var client = new Client(conString); + var client = new Client(helper.config); client.connect(); client.query("CREATE TEMP TABLE boom(name varchar(10), age integer)"); client.query("INSERT INTO boom(name, age) VALUES('Aaron', 26)"); @@ -12,7 +11,7 @@ var setupClient = function() { } test('connects', function() { - var client = new Client(conString); + var client = new Client(helper.config); client.connect(); test('good query', function() { var query = client.query("SELECT 1 as num, 'HELLO' as str"); diff --git a/test/native/stress-tests.js b/test/native/stress-tests.js index 539e667d3..cac03d037 100644 --- a/test/native/stress-tests.js +++ b/test/native/stress-tests.js @@ -2,7 +2,7 @@ var helper = require(__dirname + "/../test-helper"); var Client = require(__dirname + "/../../lib/native"); test('many rows', function() { - var client = new Client(helper.connectionString()); + var client = new Client(helper.config); client.connect(); var q = client.query("SELECT * FROM person"); var rows = []; @@ -16,7 +16,7 @@ test('many rows', function() { }); test('many queries', function() { - var client = new Client(helper.connectionString()); + var client = new Client(helper.config); client.connect(); var count = 0; var expected = 100; @@ -35,7 +35,7 @@ test('many queries', function() { test('many clients', function() { var clients = []; for(var i = 0; i < 10; i++) { - clients.push(new Client(helper.connectionString())); + clients.push(new Client(helper.config)); } clients.forEach(function(client) { client.connect(); diff --git a/test/test-helper.js b/test/test-helper.js index dbc28b5b8..55a0a5c50 100644 --- a/test/test-helper.js +++ b/test/test-helper.js @@ -225,9 +225,7 @@ module.exports = { args: args, Sink: Sink, pg: require(__dirname + '/../lib/'), - connectionString: function() { - return "pg"+(count++)+"://"+args.user+":"+args.password+"@"+args.host+":"+args.port+"/"+args.database; - }, + config: args, sys: sys, Client: Client }; diff --git a/test/unit/client/query-tests.js b/test/unit/client/query-tests.js index 36700578f..7a5487a44 100644 --- a/test/unit/client/query-tests.js +++ b/test/unit/client/query-tests.js @@ -1,7 +1,7 @@ var helper = require(__dirname + '/test-helper'); var q = {}; -q.dateParser = require(__dirname + "/../../../lib/types").getStringTypeParser(1114); -q.stringArrayParser = require(__dirname + "/../../../lib/types").getStringTypeParser(1009); +q.dateParser = require(__dirname + "/../../../lib/types").getTypeParser(1114, 'text'); +q.stringArrayParser = require(__dirname + "/../../../lib/types").getTypeParser(1009, 'text'); test("testing dateParser", function() { assert.equal(q.dateParser("2010-12-11 09:09:04").toUTCString(),new Date("2010-12-11 09:09:04 GMT").toUTCString()); diff --git a/test/unit/client/typed-query-results-tests.js b/test/unit/client/typed-query-results-tests.js index ec56020da..baa86bd4c 100644 --- a/test/unit/client/typed-query-results-tests.js +++ b/test/unit/client/typed-query-results-tests.js @@ -10,61 +10,73 @@ test('typed results', function() { //TODO refactor to this style var tests = [{ name: 'string/varchar', + format: 'text', dataTypeID: 1043, actual: 'bang', expected: 'bang' },{ name: 'integer/int4', + format: 'text', dataTypeID: 23, actual: '100', expected: 100 },{ name: 'smallint/int2', + format: 'text', dataTypeID: 21, actual: '101', expected: 101 },{ name: 'bigint/int8', + format: 'text', dataTypeID: 20, actual: '102', expected: 102 },{ name: 'oid', + format: 'text', dataTypeID: 26, actual: '103', expected: 103 },{ name: 'numeric', + format: 'text', dataTypeID: 1700, actual: '12.34', expected: 12.34 },{ name: 'real/float4', dataTypeID: 700, + format: 'text', actual: '123.456', expected: 123.456 },{ name: 'double precision / float8', + format: 'text', dataTypeID: 701, actual: '1.2', expected: 1.2 },{ name: 'boolean true', + format: 'text', dataTypeID: 16, actual: 't', expected: true },{ name: 'boolean false', + format: 'text', dataTypeID: 16, actual: 'f', expected: false },{ name: 'boolean null', + format: 'text', dataTypeID: 16, actual: null, expected: null },{ name: 'timestamptz with minutes in timezone', + format: 'text', dataTypeID: 1184, actual: '2010-10-31 14:54:13.74-0530', expected: function(val) { @@ -72,6 +84,7 @@ test('typed results', function() { } },{ name: 'timestamptz with other milisecond digits dropped', + format: 'text', dataTypeID: 1184, actual: '2011-01-23 22:05:00.68-06', expected: function(val) { @@ -79,6 +92,7 @@ test('typed results', function() { } }, { name: 'timestampz with huge miliseconds in UTC', + format: 'text', dataTypeID: 1184, actual: '2010-10-30 14:11:12.730838Z', expected: function(val) { @@ -86,6 +100,7 @@ test('typed results', function() { } },{ name: 'timestampz with no miliseconds', + format: 'text', dataTypeID: 1184, actual: '2010-10-30 13:10:01+05', expected: function(val) { @@ -93,6 +108,7 @@ test('typed results', function() { } },{ name: 'timestamp', + format: 'text', dataTypeID: 1114, actual: '2010-10-31 00:00:00', expected: function(val) { @@ -100,6 +116,7 @@ test('typed results', function() { } },{ name: 'interval time', + format: 'text', dataTypeID: 1186, actual: '01:02:03', expected: function(val) { @@ -107,6 +124,7 @@ test('typed results', function() { } },{ name: 'interval long', + format: 'text', dataTypeID: 1186, actual: '1 year -32 days', expected: function(val) { @@ -114,6 +132,7 @@ test('typed results', function() { } },{ name: 'interval combined negative', + format: 'text', dataTypeID: 1186, actual: '1 day -00:00:03', expected: function(val) { @@ -121,6 +140,7 @@ test('typed results', function() { } },{ name: 'bytea', + format: 'text', dataTypeID: 17, actual: 'foo\\000\\200\\\\\\377', expected: function(val) { @@ -128,11 +148,101 @@ test('typed results', function() { } },{ name: 'empty bytea', + format: 'text', dataTypeID: 17, actual: '', expected: function(val) { assert.deepEqual(val, new Buffer(0)); } + }, + + + { + name: 'binary-string/varchar', + format: 'binary', + dataTypeID: 1043, + actual: 'bang', + expected: 'bang' + },{ + name: 'binary-integer/int4', + format: 'binary', + dataTypeID: 23, + actual: [0, 0, 0, 100], + expected: 100 + },{ + name: 'binary-smallint/int2', + format: 'binary', + dataTypeID: 21, + actual: [0, 101], + expected: 101 + },{ + name: 'binary-bigint/int8', + format: 'binary', + dataTypeID: 20, + actual: [0, 0, 0, 0, 0, 0, 0, 102], + expected: 102 + },{ + name: 'binary-bigint/int8-full', + format: 'binary', + dataTypeID: 20, + actual: [1, 0, 0, 0, 0, 0, 0, 102], + expected: 72057594037928030 + },{ + name: 'binary-oid', + format: 'binary', + dataTypeID: 26, + actual: [0, 0, 0, 103], + expected: 103 + },{ + name: 'binary-numeric', + format: 'binary', + dataTypeID: 1700, + actual: [0,2,0,0,0,0,0,0x64,0,12,0xd,0x48,0,0,0,0], + expected: 12.34 + },{ + name: 'binary-real/float4', + dataTypeID: 700, + format: 'binary', + actual: [0x41, 0x48, 0x00, 0x00], + expected: 12.5 + },{ + name: 'binary-double precision / float8', + format: 'binary', + dataTypeID: 701, + actual: [0x3F,0xF3,0x33,0x33,0x33,0x33,0x33,0x33], + expected: 1.2 + },{ + name: 'binary-boolean true', + format: 'binary', + dataTypeID: 16, + actual: [1], + expected: true + },{ + name: 'binary-boolean false', + format: 'binary', + dataTypeID: 16, + actual: [0], + expected: false + },{ + name: 'binary-boolean null', + format: 'binary', + dataTypeID: 16, + actual: null, + expected: null + },{ + name: 'binary-timestamp', + format: 'binary', + dataTypeID: 1184, + actual: [0x00, 0x01, 0x36, 0xee, 0x3e, 0x66, 0x9f, 0xe0], + expected: function(val) { + assert.UTCDate(val, 2010, 9, 31, 20, 24, 13, 740); + } + },{ + name: 'binary-string', + format: 'binary', + dataTypeID: 25, + actual: new Buffer([0x73, 0x6c, 0x61, 0x64, 0x64, 0x61]), + expected: 'sladda' }];