commit
2f75b2f6a9
8
Makefile
8
Makefile
@ -6,7 +6,8 @@ params := $(connectionString)
|
||||
|
||||
node-command := xargs -n 1 -I file node file $(params)
|
||||
|
||||
.PHONY : test test-connection test-integration bench test-native build/default/binding.node
|
||||
.PHONY : test test-connection test-integration bench test-native \
|
||||
build/default/binding.node jshint
|
||||
|
||||
help:
|
||||
@echo "make prepare-test-db [connectionString=pg://<your connection string>]"
|
||||
@ -14,7 +15,7 @@ help:
|
||||
|
||||
test: test-unit
|
||||
|
||||
test-all: test-unit test-integration test-native test-binary
|
||||
test-all: test-unit test-integration test-native test-binary jshint
|
||||
|
||||
bench:
|
||||
@find benchmark -name "*-bench.js" | $(node-command)
|
||||
@ -50,3 +51,6 @@ prepare-test-db:
|
||||
@echo "***Preparing the database for tests***"
|
||||
@find script/create-test-tables.js | $(node-command)
|
||||
|
||||
jshint:
|
||||
@echo "***Starting jshint***"
|
||||
@./node_modules/.bin/jshint lib
|
||||
|
@ -115,6 +115,9 @@ I will __happily__ accept your pull request if it:
|
||||
- _has tests_
|
||||
- looks reasonable
|
||||
- does not break backwards compatibility
|
||||
- satisfies jshint
|
||||
|
||||
Information about the testing processes is in the [wiki](https://github.com/brianc/node-postgres/wiki/Testing).
|
||||
|
||||
If you need help or have questions about constructing a pull request I'll be glad to help out as well.
|
||||
|
||||
|
@ -28,8 +28,8 @@ ArrayParser.prototype.nextChar = function() {
|
||||
};
|
||||
}
|
||||
};
|
||||
ArrayParser.prototype.record = function(char) {
|
||||
return this.recorded.push(char);
|
||||
ArrayParser.prototype.record = function(c) {
|
||||
return this.recorded.push(c);
|
||||
};
|
||||
ArrayParser.prototype.newEntry = function(includeEmpty) {
|
||||
var entry;
|
||||
@ -47,7 +47,7 @@ ArrayParser.prototype.newEntry = function(includeEmpty) {
|
||||
};
|
||||
ArrayParser.prototype.parse = function(nested) {
|
||||
var c, p, quote;
|
||||
if (nested == null) {
|
||||
if (nested === null) {
|
||||
nested = false;
|
||||
}
|
||||
quote = false;
|
||||
@ -89,4 +89,4 @@ module.exports = {
|
||||
create: function(source, converter){
|
||||
return new ArrayParser(source, converter);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,258 +1,261 @@
|
||||
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;
|
||||
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;
|
||||
var inv = function(value) {
|
||||
if (invert) {
|
||||
return ~value & 0xff;
|
||||
}
|
||||
|
||||
if (offset) {
|
||||
mask = mask >> (offset % 8);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
var result = 0;
|
||||
if ((offset % 8) + bits >= 8) {
|
||||
result = callback(0, inv(data[offsetBytes]) & mask, firstBits);
|
||||
}
|
||||
// read first (maybe partial) byte
|
||||
var mask = 0xff;
|
||||
var firstBits = 8 - (offset % 8);
|
||||
if (bits < firstBits) {
|
||||
mask = (0xff << (8 - bits)) & 0xff;
|
||||
firstBits = bits;
|
||||
}
|
||||
|
||||
// read bytes
|
||||
var bytes = (bits + offset) >> 3;
|
||||
for (var i = offsetBytes + 1; i < bytes; i++) {
|
||||
result = callback(result, inv(data[i]), 8);
|
||||
}
|
||||
if (offset) {
|
||||
mask = mask >> (offset % 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);
|
||||
}
|
||||
var result = 0;
|
||||
if ((offset % 8) + bits >= 8) {
|
||||
result = callback(0, inv(data[offsetBytes]) & mask, firstBits);
|
||||
}
|
||||
|
||||
return result;
|
||||
// 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);
|
||||
var bias = Math.pow(2, exponentBits - 1) - 1;
|
||||
var sign = parseBits(data, 1);
|
||||
var exponent = parseBits(data, exponentBits, 1);
|
||||
|
||||
if (exponent === 0)
|
||||
return 0;
|
||||
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;
|
||||
// parse mantissa
|
||||
var precisionBitsCounter = 1;
|
||||
var parsePrecisionBits = function(lastValue, newValue, bits) {
|
||||
if (lastValue === 0) {
|
||||
lastValue = 1;
|
||||
}
|
||||
|
||||
// normale number
|
||||
return ((sign === 0) ? 1 : -1) * Math.pow(2, exponent - bias) * mantissa;
|
||||
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);
|
||||
return (parseBits(value, 8) == 1);
|
||||
};
|
||||
|
||||
var parseInt16 = function(value) {
|
||||
if (parseBits(value, 1) == 1) {
|
||||
return -1 * (parseBits(value, 15, 1, true) + 1);
|
||||
}
|
||||
if (parseBits(value, 1) == 1) {
|
||||
return -1 * (parseBits(value, 15, 1, true) + 1);
|
||||
}
|
||||
|
||||
return parseBits(value, 15, 1);
|
||||
return parseBits(value, 15, 1);
|
||||
};
|
||||
|
||||
var parseInt32 = function(value) {
|
||||
if (parseBits(value, 1) == 1) {
|
||||
return -1 * (parseBits(value, 31, 1, true) + 1);
|
||||
}
|
||||
if (parseBits(value, 1) == 1) {
|
||||
return -1 * (parseBits(value, 31, 1, true) + 1);
|
||||
}
|
||||
|
||||
return parseBits(value, 31, 1);
|
||||
return parseBits(value, 31, 1);
|
||||
};
|
||||
|
||||
var parseInt64 = function(value) {
|
||||
if (parseBits(value, 1) == 1) {
|
||||
return -1 * (parseBits(value, 63, 1, true) + 1);
|
||||
}
|
||||
if (parseBits(value, 1) == 1) {
|
||||
return -1 * (parseBits(value, 63, 1, true) + 1);
|
||||
}
|
||||
|
||||
return parseBits(value, 63, 1);
|
||||
return parseBits(value, 63, 1);
|
||||
};
|
||||
|
||||
var parseFloat32 = function(value) {
|
||||
return parseFloatFromBits(value, 23, 8);
|
||||
return parseFloatFromBits(value, 23, 8);
|
||||
};
|
||||
|
||||
var parseFloat64 = function(value) {
|
||||
return parseFloatFromBits(value, 52, 11);
|
||||
return parseFloatFromBits(value, 52, 11);
|
||||
};
|
||||
|
||||
var parseNumeric = function(value) {
|
||||
var sign = parseBits(value, 16, 32);
|
||||
if (sign == 0xc000) {
|
||||
return NaN;
|
||||
}
|
||||
var sign = parseBits(value, 16, 32);
|
||||
if (sign == 0xc000) {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
var weight = Math.pow(10000, parseBits(value, 16, 16));
|
||||
var result = 0;
|
||||
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 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 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);
|
||||
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);
|
||||
// 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;
|
||||
};
|
||||
// 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;
|
||||
return result;
|
||||
};
|
||||
|
||||
var parseArray = function(value) {
|
||||
var dim = parseBits(value, 32);
|
||||
var dim = parseBits(value, 32);
|
||||
|
||||
var flags = parseBits(value, 32, 32);
|
||||
var elementType = parseBits(value, 32, 64);
|
||||
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;
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
|
||||
var parseElement = function(elementType) {
|
||||
// parse content length
|
||||
var length = parseBits(value, 32, offset);
|
||||
offset += 32;
|
||||
var result;
|
||||
if ((elementType == 0x17) || (elementType == 0x14)) {
|
||||
// int/bigint
|
||||
result = parseBits(value, length * 8, offset);
|
||||
offset += length * 8;
|
||||
return result;
|
||||
}
|
||||
else if (elementType == 0x19) {
|
||||
// string
|
||||
result = value.toString(this.encoding, offset >> 3, (offset += (length << 3)) >> 3);
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
console.log("ERROR: ElementType not implemented: " + elementType);
|
||||
}
|
||||
};
|
||||
|
||||
// parse null values
|
||||
if (length == 0xffffffff) {
|
||||
return null;
|
||||
}
|
||||
var parse = function(dimension, elementType) {
|
||||
var array = [];
|
||||
var i;
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
if (dimension.length > 1) {
|
||||
var count = dimension.shift();
|
||||
for (i = 0; i < count; i++) {
|
||||
array[i] = parse(dimension, elementType);
|
||||
}
|
||||
dimension.unshift(count);
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < dimension[0]; i++) {
|
||||
array[i] = parseElement(elementType);
|
||||
}
|
||||
}
|
||||
|
||||
var parse = function(dimension, elementType) {
|
||||
var array = [];
|
||||
return 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);
|
||||
return parse(dims, elementType);
|
||||
};
|
||||
|
||||
var parseText = function(value) {
|
||||
return value.toString('utf8');
|
||||
return value.toString('utf8');
|
||||
};
|
||||
|
||||
var parseBool = function(value) {
|
||||
return (parseBits(value, 8) > 0);
|
||||
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);
|
||||
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
|
||||
init: init
|
||||
};
|
||||
|
@ -9,6 +9,7 @@ var defaults = require(__dirname + '/defaults');
|
||||
var Connection = require(__dirname + '/connection');
|
||||
var CopyFromStream = require(__dirname + '/copystream').CopyFromStream;
|
||||
var CopyToStream = require(__dirname + '/copystream').CopyToStream;
|
||||
|
||||
var Client = function(config) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
@ -22,7 +23,7 @@ var Client = function(config) {
|
||||
config = config || {};
|
||||
|
||||
this.connection = config.connection || new Connection({
|
||||
stream: config.stream,
|
||||
stream: config.stream,
|
||||
ssl: config.ssl
|
||||
});
|
||||
this.queryQueue = [];
|
||||
@ -112,10 +113,12 @@ p.connect = function(callback) {
|
||||
});
|
||||
con.on('copyOutResponse', function(msg) {
|
||||
if (self.activeQuery.stream === undefined) {
|
||||
self.activeQuery._canceledDueToError = new Error('No destination stream defined');
|
||||
//canceling query requires creation of new connection
|
||||
self.activeQuery._canceledDueToError =
|
||||
new Error('No destination stream defined');
|
||||
//canceling query requires creation of new connection
|
||||
//look for postgres frontend/backend protocol
|
||||
(new self.constructor({port: self.port, host: self.host})).cancel(self, self.activeQuery);
|
||||
(new self.constructor({port: self.port, host: self.host}))
|
||||
.cancel(self, self.activeQuery);
|
||||
}
|
||||
});
|
||||
con.on('copyData', function (msg) {
|
||||
@ -196,12 +199,13 @@ p._pulseQueryQueue = function() {
|
||||
this.activeQuery.submit(this.connection);
|
||||
} else if(this.hasExecuted) {
|
||||
this.activeQuery = null;
|
||||
this._drainPaused > 0 ? this._drainPaused++ : this.emit('drain')
|
||||
if(this._drainPaused > 0) { this._drainPaused++; }
|
||||
else { this.emit('drain'); }
|
||||
}
|
||||
}
|
||||
};
|
||||
p._copy = function (text, stream) {
|
||||
var config = {},
|
||||
var config = {},
|
||||
query;
|
||||
config.text = text;
|
||||
config.stream = stream;
|
||||
@ -211,7 +215,7 @@ p._copy = function (text, stream) {
|
||||
} else {
|
||||
config.stream.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
query = new Query(config);
|
||||
this.queryQueue.push(query);
|
||||
this._pulseQueryQueue();
|
||||
@ -220,13 +224,14 @@ p._copy = function (text, stream) {
|
||||
};
|
||||
p.copyFrom = function (text) {
|
||||
return this._copy(text, new CopyFromStream());
|
||||
}
|
||||
};
|
||||
p.copyTo = function (text) {
|
||||
return this._copy(text, new CopyToStream());
|
||||
}
|
||||
};
|
||||
p.query = function(config, values, callback) {
|
||||
//can take in strings, config object or query object
|
||||
var query = (config instanceof Query) ? config : new Query(config, values, callback);
|
||||
var query = (config instanceof Query) ? config :
|
||||
new Query(config, values, callback);
|
||||
if (this.binary && !query.binary) {
|
||||
query.binary = true;
|
||||
}
|
||||
@ -236,7 +241,8 @@ p.query = function(config, values, callback) {
|
||||
return query;
|
||||
};
|
||||
|
||||
//prevents client from otherwise emitting 'drain' event until 'resumeDrain' is called
|
||||
//prevents client from otherwise emitting 'drain' event until 'resumeDrain' is
|
||||
//called
|
||||
p.pauseDrain = function() {
|
||||
this._drainPaused = 1;
|
||||
};
|
||||
|
@ -4,8 +4,8 @@ var path = require('path');
|
||||
var defaults = require(__dirname + '/defaults');
|
||||
|
||||
var val = function(key, config) {
|
||||
return config[key] ||
|
||||
process.env['PG' + key.toUpperCase()] ||
|
||||
return config[key] ||
|
||||
process.env['PG' + key.toUpperCase()] ||
|
||||
defaults[key];
|
||||
};
|
||||
|
||||
@ -21,7 +21,7 @@ var parse = function(str) {
|
||||
var result = url.parse(str);
|
||||
var config = {};
|
||||
config.host = result.hostname;
|
||||
config.database = result.pathname ? result.pathname.slice(1) : null
|
||||
config.database = result.pathname ? result.pathname.slice(1) : null;
|
||||
var auth = (result.auth || ':').split(':');
|
||||
config.user = auth[0];
|
||||
config.password = auth[1];
|
||||
@ -33,7 +33,7 @@ var ConnectionParameters = function(config) {
|
||||
config = typeof config == 'string' ? parse(config) : (config || {});
|
||||
this.user = val('user', config);
|
||||
this.database = val('database', config);
|
||||
this.port = parseInt(val('port', config));
|
||||
this.port = parseInt(val('port', config), 10);
|
||||
this.host = val('host', config);
|
||||
this.password = val('password', config);
|
||||
this.binary = val('binary', config);
|
||||
@ -50,7 +50,7 @@ var add = function(params, config, paramName) {
|
||||
};
|
||||
|
||||
ConnectionParameters.prototype.getLibpqConnectionString = function(cb) {
|
||||
var params = []
|
||||
var params = [];
|
||||
add(params, this, 'user');
|
||||
add(params, this, 'password');
|
||||
add(params, this, 'port');
|
||||
@ -60,7 +60,7 @@ ConnectionParameters.prototype.getLibpqConnectionString = function(cb) {
|
||||
if(this.isDomainSocket) {
|
||||
params.push("host=" + this.getDomainSocketName());
|
||||
return cb(null, params.join(' '));
|
||||
}
|
||||
}
|
||||
dns.lookup(this.host, function(err, address) {
|
||||
if(err) return cb(err, null);
|
||||
params.push("hostaddr=" + address);
|
||||
|
@ -37,7 +37,7 @@ p.connect = function(port, host) {
|
||||
this.stream.on('connect', function() {
|
||||
self.emit('connect');
|
||||
});
|
||||
|
||||
|
||||
this.stream.on('error', function(error) {
|
||||
self.emit('error', error);
|
||||
});
|
||||
@ -53,9 +53,9 @@ p.connect = function(port, host) {
|
||||
if (msg.text == 0x53) {
|
||||
var tls = require('tls');
|
||||
self.stream.removeAllListeners();
|
||||
self.stream = tls.connect({
|
||||
socket: self.stream,
|
||||
servername: host,
|
||||
self.stream = tls.connect({
|
||||
socket: self.stream,
|
||||
servername: host,
|
||||
rejectUnauthorized: self.ssl.rejectUnauthorized,
|
||||
ca: self.ssl.ca,
|
||||
pfx: self.ssl.pfx,
|
||||
@ -67,9 +67,12 @@ p.connect = function(port, host) {
|
||||
self.attachListeners(self.stream);
|
||||
self.emit('sslconnect');
|
||||
} else {
|
||||
self.emit('error', new Error("The server doesn't support SSL/TLS connections."));
|
||||
self.emit(
|
||||
'error',
|
||||
new Error("The server doesn't support SSL/TLS connections.")
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
this.attachListeners(this.stream);
|
||||
@ -80,29 +83,30 @@ p.attachListeners = function(stream) {
|
||||
var self = this;
|
||||
stream.on('data', function(buffer) {
|
||||
self.setBuffer(buffer);
|
||||
var msg;
|
||||
while(msg = self.parseMessage()) {
|
||||
var msg = self.parseMessage();
|
||||
while(msg) {
|
||||
self.emit('message', msg);
|
||||
self.emit(msg.name, msg);
|
||||
msg = self.parseMessage();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
p.requestSsl = function(config) {
|
||||
this.checkSslResponse = true;
|
||||
|
||||
|
||||
var bodyBuffer = this.writer
|
||||
.addInt16(0x04D2)
|
||||
.addInt16(0x162F).flush();
|
||||
|
||||
|
||||
var length = bodyBuffer.length + 4;
|
||||
|
||||
|
||||
var buffer = new Writer()
|
||||
.addInt32(length)
|
||||
.add(bodyBuffer)
|
||||
.join();
|
||||
this.stream.write(buffer);
|
||||
}
|
||||
};
|
||||
|
||||
p.startup = function(config) {
|
||||
var bodyBuffer = this.writer
|
||||
@ -147,13 +151,13 @@ p.password = function(password) {
|
||||
};
|
||||
|
||||
p._send = function(code, more) {
|
||||
if(!this.stream.writable) return false;
|
||||
if(!this.stream.writable) { return false; }
|
||||
if(more === true) {
|
||||
this.writer.addHeader(code);
|
||||
} else {
|
||||
return this.stream.write(this.writer.flush(code));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
p.query = function(text) {
|
||||
//0x51 = Q
|
||||
@ -239,14 +243,14 @@ var emptyBuffer = Buffer(0);
|
||||
|
||||
p.flush = function() {
|
||||
//0x48 = 'H'
|
||||
this.writer.add(emptyBuffer)
|
||||
this.writer.add(emptyBuffer);
|
||||
this._send(0x48);
|
||||
}
|
||||
};
|
||||
|
||||
p.sync = function() {
|
||||
//clear out any pending data in the writer
|
||||
this.writer.flush(0)
|
||||
|
||||
this.writer.flush(0);
|
||||
|
||||
this.writer.add(emptyBuffer);
|
||||
this._send(0x53);
|
||||
};
|
||||
@ -263,15 +267,15 @@ p.describe = function(msg, more) {
|
||||
};
|
||||
p.sendCopyFromChunk = function (chunk) {
|
||||
this.stream.write(this.writer.add(chunk).flush(0x64));
|
||||
}
|
||||
};
|
||||
p.endCopyFrom = function () {
|
||||
this.stream.write(this.writer.add(emptyBuffer).flush(0x63));
|
||||
}
|
||||
};
|
||||
p.sendCopyFail = function (msg) {
|
||||
//this.stream.write(this.writer.add(emptyBuffer).flush(0x66));
|
||||
this.writer.addCString(msg);
|
||||
this._send(0x66);
|
||||
}
|
||||
};
|
||||
//parsing methods
|
||||
p.setBuffer = function(buffer) {
|
||||
if(this.lastBuffer) { //we have unfinished biznaz
|
||||
@ -390,7 +394,7 @@ p.parseMessage = function() {
|
||||
|
||||
case 0x48: //H
|
||||
msg.name = 'copyOutResponse';
|
||||
return this.parseGH(msg);
|
||||
return this.parseGH(msg);
|
||||
case 0x63: //c
|
||||
msg.name = 'copyDone';
|
||||
return msg;
|
||||
@ -476,8 +480,8 @@ p.parseD = function(msg) {
|
||||
var fields = [];
|
||||
for(var i = 0; i < fieldCount; i++) {
|
||||
var length = this.parseInt32();
|
||||
fields[i] = (length === -1 ? null : this.readBytes(length))
|
||||
};
|
||||
fields[i] = (length === -1 ? null : this.readBytes(length));
|
||||
}
|
||||
msg.fieldCount = fieldCount;
|
||||
msg.fields = fields;
|
||||
return msg;
|
||||
@ -539,10 +543,10 @@ p.parseGH = function (msg) {
|
||||
return msg;
|
||||
};
|
||||
p.parseInt8 = function () {
|
||||
var value = Number(this.buffer[this.offset]);
|
||||
var value = Number(this.buffer[this.offset]);
|
||||
this.offset++;
|
||||
return value;
|
||||
}
|
||||
};
|
||||
p.readChar = function() {
|
||||
return Buffer([this.buffer[this.offset++]]).toString(this.encoding);
|
||||
};
|
||||
@ -569,7 +573,8 @@ p.parseInt16 = function() {
|
||||
};
|
||||
|
||||
p.readString = function(length) {
|
||||
return this.buffer.toString(this.encoding, this.offset, (this.offset += length));
|
||||
return this.buffer.toString(this.encoding, this.offset,
|
||||
(this.offset += length));
|
||||
};
|
||||
|
||||
p.readBytes = function(length) {
|
||||
@ -578,13 +583,13 @@ p.readBytes = function(length) {
|
||||
|
||||
p.parseCString = function() {
|
||||
var start = this.offset;
|
||||
while(this.buffer[this.offset++]) { };
|
||||
while(this.buffer[this.offset++]) { }
|
||||
return this.buffer.toString(this.encoding, start, this.offset - 1);
|
||||
};
|
||||
p.parsed = function (msg) {
|
||||
//exclude length field
|
||||
msg.chunk = this.readBytes(msg.length - 4);
|
||||
return msg;
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
//end parsing methods
|
||||
module.exports = Connection;
|
||||
|
@ -14,7 +14,7 @@ var CopyFromStream = function () {
|
||||
util.inherits(CopyFromStream, Stream);
|
||||
CopyFromStream.prototype._writable = function () {
|
||||
return !(this._finished || this._error);
|
||||
}
|
||||
};
|
||||
CopyFromStream.prototype.startStreamingToConnection = function (connection) {
|
||||
if (this._error) {
|
||||
return;
|
||||
@ -33,17 +33,17 @@ CopyFromStream.prototype._handleChunk = function (string, encoding) {
|
||||
dataChunk = new Buffer(string, encoding);
|
||||
}
|
||||
if (this._buffer.length) {
|
||||
//Buffer.concat is better, but it's missing
|
||||
//Buffer.concat is better, but it's missing
|
||||
//in node v0.6.x
|
||||
tmpBuffer = new Buffer(this._buffer.length + dataChunk.length);
|
||||
this._buffer.copy(tmpBuffer);
|
||||
tmpBuffer = new Buffer(this._buffer.length + dataChunk.length);
|
||||
this._buffer.copy(tmpBuffer);
|
||||
dataChunk.copy(tmpBuffer, this._buffer.length);
|
||||
this._buffer = tmpBuffer;
|
||||
} else {
|
||||
this._buffer = dataChunk;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return this._sendIfConnectionReady();
|
||||
};
|
||||
CopyFromStream.prototype._sendIfConnectionReady = function () {
|
||||
@ -51,7 +51,7 @@ CopyFromStream.prototype._sendIfConnectionReady = function () {
|
||||
if (this._connection) {
|
||||
dataSent = this._connection.sendCopyFromChunk(this._buffer);
|
||||
this._buffer = new Buffer(0);
|
||||
if (this._dataBuffered) {
|
||||
if (this._dataBuffered) {
|
||||
this.emit('drain');
|
||||
}
|
||||
this._dataBuffered = false;
|
||||
@ -65,7 +65,7 @@ CopyFromStream.prototype._endIfNeedAndPossible = function () {
|
||||
this._finishedSent = true;
|
||||
this._connection.endCopyFrom();
|
||||
}
|
||||
}
|
||||
};
|
||||
CopyFromStream.prototype.write = function (string, encoding) {
|
||||
if (this._error || this._finished) {
|
||||
return false;
|
||||
@ -79,12 +79,12 @@ CopyFromStream.prototype.end = function (string, encondig) {
|
||||
this._finished = true;
|
||||
if (string !== undefined) {
|
||||
this._handleChunk.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
this._endIfNeedAndPossible();
|
||||
};
|
||||
CopyFromStream.prototype.error = function (error) {
|
||||
if (this._error || this._closed) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
this._error = true;
|
||||
this.emit('error', error);
|
||||
@ -123,7 +123,7 @@ CopyToStream.prototype._outputDataChunk = function () {
|
||||
};
|
||||
CopyToStream.prototype._readable = function () {
|
||||
return !this._finished && !this._error;
|
||||
}
|
||||
};
|
||||
CopyToStream.prototype.error = function (error) {
|
||||
if (!this.readable) {
|
||||
return false;
|
||||
@ -171,7 +171,7 @@ CopyToStream.prototype.resume = function () {
|
||||
this._outputDataChunk();
|
||||
if (this._error) {
|
||||
return this.emit('error', this._error);
|
||||
}
|
||||
}
|
||||
if (this._finished) {
|
||||
return this.emit('end');
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ module.exports = {
|
||||
|
||||
//database user's password
|
||||
password: null,
|
||||
|
||||
|
||||
//database port
|
||||
port: 5432,
|
||||
|
||||
@ -17,7 +17,7 @@ module.exports = {
|
||||
|
||||
// binary result mode
|
||||
binary: false,
|
||||
|
||||
|
||||
//Connection pool options - see https://github.com/coopernurse/node-pool
|
||||
//number of connections to use in connection pool
|
||||
//0 will disable connection pooling
|
||||
@ -32,4 +32,4 @@ module.exports = {
|
||||
|
||||
//pool log function / boolean
|
||||
poolLog: false
|
||||
}
|
||||
};
|
||||
|
26
lib/index.js
26
lib/index.js
@ -13,7 +13,7 @@ var PG = function(clientConstructor) {
|
||||
EventEmitter.call(this);
|
||||
this.Client = clientConstructor;
|
||||
this.Connection = require(__dirname + '/connection');
|
||||
this.Query = clientConstructor.Query
|
||||
this.Query = clientConstructor.Query;
|
||||
this.defaults = defaults;
|
||||
};
|
||||
|
||||
@ -25,8 +25,8 @@ PG.prototype.end = function() {
|
||||
pool.drain(function() {
|
||||
pool.destroyAllNow();
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
PG.prototype.connect = function(config, callback) {
|
||||
var self = this;
|
||||
@ -42,15 +42,15 @@ PG.prototype.connect = function(config, callback) {
|
||||
var poolName = typeof(c) === 'string' ? c : c.user+c.host+c.port+c.database;
|
||||
var pool = pools[poolName];
|
||||
|
||||
if(pool) return pool.acquire(cb);
|
||||
if(pool) { return pool.acquire(cb); }
|
||||
|
||||
var pool = pools[poolName] = genericPool.Pool({
|
||||
pool = pools[poolName] = genericPool.Pool({
|
||||
name: poolName,
|
||||
create: function(callback) {
|
||||
var client = new self.Client(c);
|
||||
client.connect(function(err) {
|
||||
if(err) return callback(err);
|
||||
|
||||
if(err) { return callback(err); }
|
||||
|
||||
//handle connected client background errors by emitting event
|
||||
//via the pg object and then removing errored client from the pool
|
||||
client.on('error', function(e) {
|
||||
@ -74,24 +74,26 @@ PG.prototype.connect = function(config, callback) {
|
||||
log: defaults.poolLog
|
||||
});
|
||||
return pool.acquire(cb);
|
||||
}
|
||||
};
|
||||
|
||||
// cancel the query runned by the given client
|
||||
PG.prototype.cancel = function(config, client, query) {
|
||||
var c = config;
|
||||
//allow for no config to be passed
|
||||
if(typeof c === 'function')
|
||||
if(typeof c === 'function') {
|
||||
c = defaults;
|
||||
}
|
||||
var cancellingClient = new this.Client(c);
|
||||
cancellingClient.cancel(client, query);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = new PG(Client);
|
||||
|
||||
//lazy require native module...the native module may not have installed
|
||||
//lazy require native module...the native module may not have installed
|
||||
module.exports.__defineGetter__("native", function() {
|
||||
delete module.exports.native;
|
||||
return (module.exports.native = new PG(require(__dirname + '/native')));
|
||||
module.exports.native = new PG(require(__dirname + '/native'));
|
||||
return module.exports.native;
|
||||
});
|
||||
|
||||
module.exports.types = require('./types');
|
||||
|
@ -8,12 +8,12 @@ var CopyToStream = require(__dirname + '/../copystream').CopyToStream;
|
||||
|
||||
var binding;
|
||||
|
||||
try{
|
||||
try {
|
||||
//v0.5.x
|
||||
binding = require(__dirname + '/../../build/Release/binding.node');
|
||||
binding = require(__dirname + '/../../build/Release/binding.node');
|
||||
} catch(e) {
|
||||
//v0.4.x
|
||||
binding = require(__dirname + '/../../build/default/binding');
|
||||
binding = require(__dirname + '/../../build/default/binding');
|
||||
}
|
||||
|
||||
var Connection = binding.Connection;
|
||||
@ -40,18 +40,18 @@ p.connect = function(cb) {
|
||||
//remove single-fire connection error callback
|
||||
self.removeListener('error', errCallback);
|
||||
cb(null);
|
||||
}
|
||||
};
|
||||
errCallback = function(err) {
|
||||
//remove singel-fire connection success callback
|
||||
self.removeListener('connect', connectCallback);
|
||||
cb(err);
|
||||
}
|
||||
};
|
||||
self.once('connect', connectCallback);
|
||||
self.once('error', errCallback);
|
||||
}
|
||||
nativeConnect.call(self, conString);
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
p._copy = function (text, stream) {
|
||||
var q = new NativeQuery(text, function (error) {
|
||||
if (error) {
|
||||
@ -59,12 +59,12 @@ p._copy = function (text, stream) {
|
||||
} else {
|
||||
q.stream.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
q.stream = stream;
|
||||
this._queryQueue.push(q);
|
||||
this._pulseQueryQueue();
|
||||
return q.stream;
|
||||
}
|
||||
};
|
||||
p.copyFrom = function (text) {
|
||||
return this._copy(text, new CopyFromStream());
|
||||
};
|
||||
@ -72,17 +72,18 @@ p.copyTo = function (text) {
|
||||
return this._copy(text, new CopyToStream());
|
||||
};
|
||||
p.sendCopyFromChunk = function (chunk) {
|
||||
this._sendCopyFromChunk(chunk);
|
||||
this._sendCopyFromChunk(chunk);
|
||||
};
|
||||
p.endCopyFrom = function (msg) {
|
||||
this._endCopyFrom(msg);
|
||||
};
|
||||
p.query = function(config, values, callback) {
|
||||
var query = (config instanceof NativeQuery) ? config : new NativeQuery(config, values, callback);
|
||||
var query = (config instanceof NativeQuery) ? config :
|
||||
new NativeQuery(config, values, callback);
|
||||
this._queryQueue.push(query);
|
||||
this._pulseQueryQueue();
|
||||
return query;
|
||||
}
|
||||
};
|
||||
|
||||
var nativeCancel = p.cancel;
|
||||
|
||||
@ -103,7 +104,11 @@ p._pulseQueryQueue = function(initialConnection) {
|
||||
var query = this._queryQueue.shift();
|
||||
if(!query) {
|
||||
if(!initialConnection) {
|
||||
this._drainPaused ? this._drainPaused++ : this.emit('drain');
|
||||
if(this._drainPaused) {
|
||||
this._drainPaused++;
|
||||
} else {
|
||||
this.emit('drain');
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -119,12 +124,12 @@ p._pulseQueryQueue = function(initialConnection) {
|
||||
}
|
||||
else if(query.values) {
|
||||
//call native function
|
||||
this._sendQueryWithParams(query.text, query.values)
|
||||
this._sendQueryWithParams(query.text, query.values);
|
||||
} else {
|
||||
//call native function
|
||||
this._sendQuery(query.text);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
p.pauseDrain = function() {
|
||||
this._drainPaused = 1;
|
||||
@ -132,8 +137,8 @@ p.pauseDrain = function() {
|
||||
|
||||
p.resumeDrain = function() {
|
||||
if(this._drainPaused > 1) {
|
||||
this.emit('drain')
|
||||
};
|
||||
this.emit('drain');
|
||||
}
|
||||
this._drainPaused = 0;
|
||||
};
|
||||
p.sendCopyFail = function(msg) {
|
||||
@ -199,14 +204,16 @@ var clientBuilder = function(config) {
|
||||
});
|
||||
connection.on('copyInResponse', function () {
|
||||
//connection is ready to accept chunks
|
||||
//start to send data from stream
|
||||
//start to send data from stream
|
||||
connection._activeQuery.streamData(connection);
|
||||
});
|
||||
connection.on('copyOutResponse', function(msg) {
|
||||
if (connection._activeQuery.stream === undefined) {
|
||||
connection._activeQuery._canceledDueToError = new Error('No destination stream defined');
|
||||
(new clientBuilder({port: connection.port, host: connection.host})).cancel(connection, connection._activeQuery);
|
||||
}
|
||||
if (connection._activeQuery.stream === undefined) {
|
||||
connection._activeQuery._canceledDueToError =
|
||||
new Error('No destination stream defined');
|
||||
(new clientBuilder({port: connection.port, host: connection.host}))
|
||||
.cancel(connection, connection._activeQuery);
|
||||
}
|
||||
});
|
||||
connection.on('copyData', function (chunk) {
|
||||
//recieve chunk from connection
|
||||
|
@ -8,7 +8,9 @@ var Result = require(__dirname + '/../result');
|
||||
//event emitter proxy
|
||||
var NativeQuery = function(config, values, callback) {
|
||||
// use of "new" optional
|
||||
if (!(this instanceof NativeQuery)) return new NativeQuery(config, values, callback);
|
||||
if (!(this instanceof NativeQuery)) {
|
||||
return new NativeQuery(config, values, callback);
|
||||
}
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
@ -18,7 +20,7 @@ var NativeQuery = function(config, values, callback) {
|
||||
this.text = config.text;
|
||||
this.values = config.values;
|
||||
this.callback = config.callback;
|
||||
|
||||
|
||||
this._result = new Result();
|
||||
//normalize values
|
||||
if(this.values) {
|
||||
@ -36,11 +38,12 @@ var p = NativeQuery.prototype;
|
||||
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.getTypeParser(item.type, 'text')(item.value);
|
||||
var item = row[i];
|
||||
result[item.name] = item.value === null ? null :
|
||||
types.getTypeParser(item.type, 'text')(item.value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
p.handleRow = function(rowData) {
|
||||
var row = mapRowData(rowData);
|
||||
@ -61,7 +64,7 @@ p.handleError = function(error) {
|
||||
} else {
|
||||
this.emit('error', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
p.handleReadyForQuery = function(meta) {
|
||||
if (this._canceledDueToError) {
|
||||
@ -77,14 +80,14 @@ p.handleReadyForQuery = function(meta) {
|
||||
};
|
||||
p.streamData = function (connection) {
|
||||
if ( this.stream ) this.stream.startStreamingToConnection(connection);
|
||||
else connection.sendCopyFail('No source stream defined');
|
||||
else connection.sendCopyFail('No source stream defined');
|
||||
};
|
||||
p.handleCopyFromChunk = function (chunk) {
|
||||
if ( this.stream ) {
|
||||
this.stream.handleChunk(chunk);
|
||||
}
|
||||
//if there are no stream (for example when copy to query was sent by
|
||||
//query method instead of copyTo) error will be handled
|
||||
//on copyOutResponse event, so silently ignore this error here
|
||||
}
|
||||
//query method instead of copyTo) error will be handled
|
||||
//on copyOutResponse event, so silently ignore this error here
|
||||
};
|
||||
module.exports = NativeQuery;
|
||||
|
32
lib/query.js
32
lib/query.js
@ -7,10 +7,10 @@ var utils = require(__dirname + '/utils');
|
||||
|
||||
var Query = function(config, values, callback) {
|
||||
// use of "new" optional
|
||||
if (!(this instanceof Query)) return new Query(config, values, callback);
|
||||
|
||||
if (!(this instanceof Query)) { return new Query(config, values, callback); }
|
||||
|
||||
config = utils.normalizeQueryConfig(config, values, callback);
|
||||
|
||||
|
||||
this.text = config.text;
|
||||
this.values = config.values;
|
||||
this.rows = config.rows;
|
||||
@ -19,7 +19,7 @@ var Query = function(config, values, callback) {
|
||||
this.binary = config.binary;
|
||||
this.stream = config.stream;
|
||||
//use unique portal name each time
|
||||
this.portal = config.portal || ""
|
||||
this.portal = config.portal || "";
|
||||
this.callback = config.callback;
|
||||
this._fieldNames = [];
|
||||
this._fieldConverters = [];
|
||||
@ -34,15 +34,15 @@ var p = Query.prototype;
|
||||
|
||||
p.requiresPreparation = function() {
|
||||
//named queries must always be prepared
|
||||
if(this.name) return true;
|
||||
if(this.name) { return true; }
|
||||
//always prepare if there are max number of rows expected per
|
||||
//portal execution
|
||||
if(this.rows) return true;
|
||||
//portal execution
|
||||
if(this.rows) { return true; }
|
||||
//don't prepare empty text queries
|
||||
if(!this.text) return false;
|
||||
if(!this.text) { return false; }
|
||||
//binary should be prepared to specify results should be in binary
|
||||
//unless there are no parameters
|
||||
if(this.binary && !this.values) return false;
|
||||
if(this.binary && !this.values) { return false; }
|
||||
//prepare if there are values
|
||||
return (this.values || 0).length > 0;
|
||||
};
|
||||
@ -64,7 +64,7 @@ p.handleRowDescription = function(msg) {
|
||||
var format = field.format;
|
||||
this._fieldNames[i] = field.name;
|
||||
this._fieldConverters[i] = Types.getTypeParser(field.dataTypeID, format);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
p.handleDataRow = function(msg) {
|
||||
@ -110,7 +110,7 @@ p.handleError = function(err) {
|
||||
//if callback supplied do not emit error event as uncaught error
|
||||
//events will bubble up to node process
|
||||
if(this.callback) {
|
||||
this.callback(err)
|
||||
this.callback(err);
|
||||
} else {
|
||||
this.emit('error', err);
|
||||
}
|
||||
@ -179,14 +179,14 @@ p.prepare = function(connection) {
|
||||
};
|
||||
p.streamData = function (connection) {
|
||||
if ( this.stream ) this.stream.startStreamingToConnection(connection);
|
||||
else connection.sendCopyFail('No source stream defined');
|
||||
else connection.sendCopyFail('No source stream defined');
|
||||
};
|
||||
p.handleCopyFromChunk = function (chunk) {
|
||||
if ( this.stream ) {
|
||||
this.stream.handleChunk(chunk);
|
||||
}
|
||||
}
|
||||
//if there are no stream (for example when copy to query was sent by
|
||||
//query method instead of copyTo) error will be handled
|
||||
//on copyOutResponse event, so silently ignore this error here
|
||||
}
|
||||
//query method instead of copyTo) error will be handled
|
||||
//on copyOutResponse event, so silently ignore this error here
|
||||
};
|
||||
module.exports = Query;
|
||||
|
@ -10,26 +10,27 @@ var Result = function() {
|
||||
|
||||
var p = Result.prototype;
|
||||
|
||||
var matchRegexp = /([A-Za-z]+) (\d+ )?(\d+)?/
|
||||
var matchRegexp = /([A-Za-z]+) (\d+ )?(\d+)?/;
|
||||
|
||||
//adds a command complete message
|
||||
p.addCommandComplete = function(msg) {
|
||||
var match;
|
||||
if(msg.text) {
|
||||
//pure javascript
|
||||
var match = matchRegexp.exec(msg.text);
|
||||
match = matchRegexp.exec(msg.text);
|
||||
} else {
|
||||
//native bindings
|
||||
var match = matchRegexp.exec(msg.command);
|
||||
match = matchRegexp.exec(msg.command);
|
||||
}
|
||||
if(match) {
|
||||
this.command = match[1];
|
||||
//match 3 will only be existing on insert commands
|
||||
if(match[3]) {
|
||||
//msg.value is from native bindings
|
||||
this.rowCount = parseInt(match[3] || msg.value);
|
||||
this.oid = parseInt(match[2]);
|
||||
this.rowCount = parseInt(match[3] || msg.value, 10);
|
||||
this.oid = parseInt(match[2], 10);
|
||||
} else {
|
||||
this.rowCount = parseInt(match[2]);
|
||||
this.rowCount = parseInt(match[2], 10);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -3,7 +3,8 @@ var arrayParser = require(__dirname + "/arrayParser.js");
|
||||
//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 dateMatcher =
|
||||
/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?/;
|
||||
|
||||
var match = dateMatcher.exec(isoDate);
|
||||
//could not parse date
|
||||
@ -37,7 +38,8 @@ var parseDate = function(isoDate) {
|
||||
if(tZone) {
|
||||
var type = tZone[1];
|
||||
switch(type) {
|
||||
case 'Z': break;
|
||||
case 'Z':
|
||||
break;
|
||||
case '-':
|
||||
tzAdjust = -(((parseInt(tZone[2],10)*60)+(parseInt(tZone[3]||0,10))));
|
||||
break;
|
||||
@ -59,33 +61,35 @@ var parseDate = function(isoDate) {
|
||||
|
||||
var parseBool = function(val) {
|
||||
return val === 't';
|
||||
}
|
||||
};
|
||||
|
||||
var parseIntegerArray = function(val) {
|
||||
if(!val) return null;
|
||||
if(!val) { return null; }
|
||||
var p = arrayParser.create(val, function(entry){
|
||||
if(entry != null)
|
||||
if(entry !== null) {
|
||||
entry = parseInt(entry, 10);
|
||||
}
|
||||
return entry;
|
||||
});
|
||||
|
||||
|
||||
return p.parse();
|
||||
};
|
||||
|
||||
var parseFloatArray = function(val) {
|
||||
if(!val) return null;
|
||||
if(!val) { return null; }
|
||||
var p = arrayParser.create(val, function(entry){
|
||||
if(entry != null)
|
||||
if(entry !== null) {
|
||||
entry = parseFloat(entry, 10);
|
||||
}
|
||||
return entry;
|
||||
});
|
||||
|
||||
|
||||
return p.parse();
|
||||
};
|
||||
|
||||
var parseStringArray = function(val) {
|
||||
if(!val) return null;
|
||||
|
||||
if(!val) { return null; }
|
||||
|
||||
var p = arrayParser.create(val);
|
||||
return p.parse();
|
||||
};
|
||||
@ -96,26 +100,29 @@ 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 INTERVAL = [YEAR,MON,DAY,TIME].map(function(p){
|
||||
return "("+p+")?";
|
||||
}).join('\\s*');
|
||||
|
||||
var parseInterval = function(val) {
|
||||
if (!val) return {};
|
||||
if (!val) { return {}; }
|
||||
var m = new RegExp(INTERVAL).exec(val);
|
||||
var i = {};
|
||||
if (m[2]) i.years = parseInt(m[2], 10);
|
||||
if (m[4]) i.months = parseInt(m[4], 10);
|
||||
if (m[6]) i.days = parseInt(m[6], 10);
|
||||
if (m[9]) i.hours = parseInt(m[9], 10);
|
||||
if (m[10]) i.minutes = parseInt(m[10], 10);
|
||||
if (m[11]) i.seconds = parseInt(m[11], 10);
|
||||
if (m[2]) { i.years = parseInt(m[2], 10); }
|
||||
if (m[4]) { i.months = parseInt(m[4], 10); }
|
||||
if (m[6]) { i.days = parseInt(m[6], 10); }
|
||||
if (m[9]) { i.hours = parseInt(m[9], 10); }
|
||||
if (m[10]) { i.minutes = parseInt(m[10], 10); }
|
||||
if (m[11]) { i.seconds = parseInt(m[11], 10); }
|
||||
if (m[8] == '-'){
|
||||
if (i.hours) i.hours *= -1;
|
||||
if (i.minutes) i.minutes *= -1;
|
||||
if (i.seconds) i.seconds *= -1;
|
||||
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];
|
||||
for (var field in i){
|
||||
if (i[field] === 0) {
|
||||
delete i[field];
|
||||
}
|
||||
}
|
||||
return i;
|
||||
};
|
||||
@ -125,67 +132,69 @@ var parseByteA = function(val) {
|
||||
// new 'hex' style response (pg >9.0)
|
||||
return new Buffer(val.substr(2), 'hex');
|
||||
}else{
|
||||
out = ""
|
||||
i = 0
|
||||
var out = "";
|
||||
var i = 0;
|
||||
while(i < val.length){
|
||||
if(val[i] != "\\"){
|
||||
out += val[i]
|
||||
++i
|
||||
out += val[i];
|
||||
++i;
|
||||
}else{
|
||||
if(val.substr(i+1,3).match(/[0-7]{3}/)){
|
||||
out += String.fromCharCode(parseInt(val.substr(i+1,3),8))
|
||||
i += 4
|
||||
out += String.fromCharCode(parseInt(val.substr(i+1,3),8));
|
||||
i += 4;
|
||||
}else{
|
||||
backslashes = 1
|
||||
backslashes = 1;
|
||||
while(i+backslashes < val.length && val[i+backslashes] == "\\")
|
||||
backslashes++
|
||||
backslashes++;
|
||||
for(k=0; k<Math.floor(backslashes/2); ++k)
|
||||
out += "\\"
|
||||
i += Math.floor(backslashes / 2) * 2
|
||||
out += "\\";
|
||||
i += Math.floor(backslashes / 2) * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Buffer(out,"binary");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var maxLen = Number.MAX_VALUE.toString().length
|
||||
var maxLen = Number.MAX_VALUE.toString().length;
|
||||
|
||||
var parseInteger = function(val) {
|
||||
return parseInt(val, 10);
|
||||
}
|
||||
};
|
||||
|
||||
var init = function(register) {
|
||||
register(20, parseInteger);
|
||||
register(21, parseInteger);
|
||||
register(23, parseInteger);
|
||||
register(26, parseInteger);
|
||||
register(1700, function(val){
|
||||
if(val.length > maxLen) {
|
||||
console.warn('WARNING: value %s is longer than max supported numeric value in javascript. Possible data loss', val)
|
||||
}
|
||||
return parseFloat(val);
|
||||
});
|
||||
register(700, parseFloat);
|
||||
register(701, parseFloat);
|
||||
register(16, parseBool);
|
||||
register(1082, parseDate); // date
|
||||
register(1114, parseDate); // timestamp without timezone
|
||||
register(1184, parseDate); // timestamp
|
||||
register(1005, parseIntegerArray); // _int2
|
||||
register(1007, parseIntegerArray); // _int4
|
||||
register(1016, parseIntegerArray); // _int8
|
||||
register(1021, parseFloatArray); // _float4
|
||||
register(1022, parseFloatArray); // _float8
|
||||
register(1231, parseIntegerArray); // _numeric
|
||||
register(1014, parseStringArray); //char
|
||||
register(1015, parseStringArray); //varchar
|
||||
register(1008, parseStringArray);
|
||||
register(1009, parseStringArray);
|
||||
register(1186, parseInterval);
|
||||
register(17, parseByteA);
|
||||
register(20, parseInteger);
|
||||
register(21, parseInteger);
|
||||
register(23, parseInteger);
|
||||
register(26, parseInteger);
|
||||
register(1700, function(val){
|
||||
if(val.length > maxLen) {
|
||||
console.warn(
|
||||
'WARNING: value %s is longer than max supported numeric value in ' +
|
||||
'javascript. Possible data loss', val);
|
||||
}
|
||||
return parseFloat(val);
|
||||
});
|
||||
register(700, parseFloat);
|
||||
register(701, parseFloat);
|
||||
register(16, parseBool);
|
||||
register(1082, parseDate); // date
|
||||
register(1114, parseDate); // timestamp without timezone
|
||||
register(1184, parseDate); // timestamp
|
||||
register(1005, parseIntegerArray); // _int2
|
||||
register(1007, parseIntegerArray); // _int4
|
||||
register(1016, parseIntegerArray); // _int8
|
||||
register(1021, parseFloatArray); // _float4
|
||||
register(1022, parseFloatArray); // _float8
|
||||
register(1231, parseIntegerArray); // _numeric
|
||||
register(1014, parseStringArray); //char
|
||||
register(1015, parseStringArray); //varchar
|
||||
register(1008, parseStringArray);
|
||||
register(1009, parseStringArray);
|
||||
register(1186, parseInterval);
|
||||
register(17, parseByteA);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
init: init,
|
||||
init: init
|
||||
};
|
||||
|
@ -9,13 +9,14 @@ var typeParsers = {
|
||||
//the empty parse function
|
||||
var noParse = function(val) {
|
||||
return String(val);
|
||||
}
|
||||
};
|
||||
|
||||
//returns a function used to convert a specific type (specified by
|
||||
//oid) into a result javascript type
|
||||
var getTypeParser = function(oid, format) {
|
||||
if (!typeParsers[format])
|
||||
if (!typeParsers[format]) {
|
||||
return noParse;
|
||||
}
|
||||
|
||||
return typeParsers[format][oid] || noParse;
|
||||
};
|
||||
@ -26,7 +27,7 @@ var setTypeParser = function(oid, format, parseFn) {
|
||||
format = 'text';
|
||||
}
|
||||
typeParsers[format][oid] = parseFn;
|
||||
}
|
||||
};
|
||||
|
||||
textParsers.init(function(oid, converter) {
|
||||
typeParsers.text[oid] = function(value) {
|
||||
@ -41,4 +42,4 @@ binaryParsers.init(function(oid, converter) {
|
||||
module.exports = {
|
||||
getTypeParser: getTypeParser,
|
||||
setTypeParser: setTypeParser
|
||||
}
|
||||
};
|
||||
|
@ -25,7 +25,7 @@ var prepareValue = function(val) {
|
||||
return null;
|
||||
}
|
||||
return val === null ? null : val.toString();
|
||||
}
|
||||
};
|
||||
|
||||
function normalizeQueryConfig (config, values, callback) {
|
||||
//can take in strings or config objects
|
||||
@ -46,4 +46,4 @@ function normalizeQueryConfig (config, values, callback) {
|
||||
module.exports = {
|
||||
prepareValue: prepareValue,
|
||||
normalizeQueryConfig: normalizeQueryConfig
|
||||
}
|
||||
};
|
||||
|
@ -18,34 +18,34 @@ p._ensure = function(size) {
|
||||
this.buffer = new Buffer(oldBuffer.length + size);
|
||||
oldBuffer.copy(this.buffer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
p.addInt32 = function(num) {
|
||||
this._ensure(4)
|
||||
this.buffer[this.offset++] = (num >>> 24 & 0xFF)
|
||||
this.buffer[this.offset++] = (num >>> 16 & 0xFF)
|
||||
this.buffer[this.offset++] = (num >>> 8 & 0xFF)
|
||||
this.buffer[this.offset++] = (num >>> 0 & 0xFF)
|
||||
this._ensure(4);
|
||||
this.buffer[this.offset++] = (num >>> 24 & 0xFF);
|
||||
this.buffer[this.offset++] = (num >>> 16 & 0xFF);
|
||||
this.buffer[this.offset++] = (num >>> 8 & 0xFF);
|
||||
this.buffer[this.offset++] = (num >>> 0 & 0xFF);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
p.addInt16 = function(num) {
|
||||
this._ensure(2)
|
||||
this.buffer[this.offset++] = (num >>> 8 & 0xFF)
|
||||
this.buffer[this.offset++] = (num >>> 0 & 0xFF)
|
||||
this._ensure(2);
|
||||
this.buffer[this.offset++] = (num >>> 8 & 0xFF);
|
||||
this.buffer[this.offset++] = (num >>> 0 & 0xFF);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
//for versions of node requiring 'length' as 3rd argument to buffer.write
|
||||
var writeString = function(buffer, string, offset, len) {
|
||||
buffer.write(string, offset, len);
|
||||
}
|
||||
};
|
||||
|
||||
//overwrite function for older versions of node
|
||||
if(Buffer.prototype.write.length === 3) {
|
||||
writeString = function(buffer, string, offset, len) {
|
||||
buffer.write(string, offset);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
p.addCString = function(string) {
|
||||
@ -61,40 +61,40 @@ p.addCString = function(string) {
|
||||
|
||||
this.buffer[this.offset++] = 0; // null terminator
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
p.addChar = function(char) {
|
||||
p.addChar = function(c) {
|
||||
this._ensure(1);
|
||||
writeString(this.buffer, char, this.offset, 1);
|
||||
writeString(this.buffer, c, this.offset, 1);
|
||||
this.offset++;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
p.addString = function(string) {
|
||||
var string = string || "";
|
||||
string = string || "";
|
||||
var len = Buffer.byteLength(string);
|
||||
this._ensure(len);
|
||||
this.buffer.write(string, this.offset);
|
||||
this.offset += len;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
p.getByteLength = function() {
|
||||
return this.offset - 5;
|
||||
}
|
||||
};
|
||||
|
||||
p.add = function(otherBuffer) {
|
||||
this._ensure(otherBuffer.length);
|
||||
otherBuffer.copy(this.buffer, this.offset);
|
||||
this.offset += otherBuffer.length;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
p.clear = function() {
|
||||
this.offset = 5;
|
||||
this.headerPosition = 0;
|
||||
this.lastEnd = 0;
|
||||
}
|
||||
};
|
||||
|
||||
//appends a header block to all the written data since the last
|
||||
//subsequent header or to the beginning if there is only one data block
|
||||
@ -103,7 +103,7 @@ p.addHeader = function(code, last) {
|
||||
this.offset = this.headerPosition;
|
||||
this.buffer[this.offset++] = code;
|
||||
//length is everything in this packet minus the code
|
||||
this.addInt32(origOffset - (this.headerPosition+1))
|
||||
this.addInt32(origOffset - (this.headerPosition+1));
|
||||
//set next header position
|
||||
this.headerPosition = origOffset;
|
||||
//make space for next header
|
||||
@ -112,19 +112,19 @@ p.addHeader = function(code, last) {
|
||||
this._ensure(5);
|
||||
this.offset += 5;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
p.join = function(code) {
|
||||
if(code) {
|
||||
this.addHeader(code, true);
|
||||
}
|
||||
return this.buffer.slice(code ? 0 : 5, this.offset);
|
||||
}
|
||||
};
|
||||
|
||||
p.flush = function(code) {
|
||||
var result = this.join(code);
|
||||
this.clear();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Writer;
|
||||
|
@ -12,7 +12,10 @@
|
||||
"dependencies" : {
|
||||
"generic-pool" : "2.0.2"
|
||||
},
|
||||
"scripts" : {
|
||||
"devDependencies" : {
|
||||
"jshint" : "git://github.com/jshint/jshint.git"
|
||||
},
|
||||
"scripts" : {
|
||||
"test" : "make test-all connectionString=pg://postgres@localhost:5432/postgres",
|
||||
"prepublish": "rm -r build || (exit 0)",
|
||||
"install" : "node-gyp rebuild || (exit 0)"
|
||||
|
Loading…
Reference in New Issue
Block a user