Merge pull request #256 from booo/jshint

Introduce Jshint
This commit is contained in:
Brian C 2013-01-24 18:49:05 -08:00
commit 2f75b2f6a9
20 changed files with 469 additions and 418 deletions

4
.jshintrc Normal file
View File

@ -0,0 +1,4 @@
{
"trailing": true,
"indent": 2
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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