Skip to content

Would you like to pull my changes? #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 42 commits into from
Nov 28, 2011
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
1e61247
conversion pg text to binary protocol started
AlexanderS Jan 27, 2011
d553881
added date parsing
AlexanderS Jan 27, 2011
37e958f
added arrayParser
AlexanderS Jan 28, 2011
b98994a
removed debug output
AlexanderS Jan 28, 2011
01e0fb1
added microseconds to date
AlexanderS Jan 28, 2011
fa35c13
Merge remote branch 'upstream/master'
AlexanderS Jan 29, 2011
fec176e
fixed typo
AlexanderS Jan 29, 2011
0d36ce0
added numeric parser, modularized parsers
AlexanderS Jan 29, 2011
96caba5
fixed off by one
AlexanderS Feb 1, 2011
df326ec
fixed bool parsing
AlexanderS Feb 1, 2011
5b3c501
use config
AlexanderS Feb 1, 2011
8872480
removed debug
AlexanderS Feb 1, 2011
a0be34d
corrected typo
AlexanderS Feb 7, 2011
a9e40a2
fix typo
AlexanderS Feb 14, 2011
796b8df
added option for using the binary format
AlexanderS Feb 14, 2011
c731cd2
added support for bigint array type
AlexanderS Mar 2, 2011
67cb9f8
Merge remote branch 'upstream/master'
AlexanderS Mar 2, 2011
acdd726
fixed merge error
AlexanderS Mar 3, 2011
ba9b85f
binaryParser: added function to parse text fields
AlexanderS Jun 6, 2011
c513780
textParser: fix error
AlexanderS Jun 6, 2011
671a5c5
binaryParser: fixed text protocoll
AlexanderS Jun 7, 2011
e891e7f
parser: added bool parsing
AlexanderS Jun 16, 2011
abaa4a1
tests: added some tests for binary parser
AlexanderS Jun 16, 2011
a8acf9a
tests: added test for binary null
AlexanderS Jun 19, 2011
727de59
tests: added test for binary timestamp
AlexanderS Jun 19, 2011
207b7db
Merge remote branch 'upstream/master'
AlexanderS Jun 21, 2011
36243af
tests: fixed bug, because of renamed function
AlexanderS Jul 10, 2011
9168956
Merge remote branch 'upstream/master'
AlexanderS Nov 18, 2011
59c5df6
remove name duplication
AlexanderS Nov 18, 2011
8730a31
Merge remote branch 'upstream/master'
AlexanderS Nov 18, 2011
aff94b0
removed merge artifact
AlexanderS Nov 20, 2011
e9838cc
fix textParsers
AlexanderS Nov 21, 2011
070155a
fix native bindings
AlexanderS Nov 21, 2011
f3c9a53
code beautification
AlexanderS Nov 21, 2011
09ee46d
fix binaryParsers: oid is 32bit
AlexanderS Nov 22, 2011
5d8c8bb
fix recognition of query format for empty queries in dictionary format
AlexanderS Nov 21, 2011
f8962fd
connection can be binary by default
AlexanderS Nov 21, 2011
239d8bd
fixed binaryParsers for small negativ values
AlexanderS Nov 22, 2011
f698ed4
use config dict in all test
AlexanderS Nov 22, 2011
2b7c577
add binary cli argument for tests
AlexanderS Nov 22, 2011
6b032c4
added test-binary target
AlexanderS Nov 22, 2011
b2a2d02
fixed test, column should be accessed with name
AlexanderS Nov 22, 2011
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ node-command := xargs -n 1 -I file node file $(params)
.PHONY : test test-connection test-integration bench test-native build/default/binding.node
test: test-unit

test-all: test-unit test-integration test-native
test-all: test-unit test-integration test-native test-binary

bench:
@find benchmark -name "*-bench.js" | $(node-command)
Expand All @@ -28,6 +28,9 @@ test-unit:
test-connection:
@node script/test-connection.js $(params)

test-connection-binary:
@node script/test-connection.js $(params) --binary true

test-native: build/default/binding.node
@echo "***Testing native bindings***"
@find test/native -name "*-tests.js" | $(node-command)
Expand All @@ -36,3 +39,7 @@ test-native: build/default/binding.node
test-integration: test-connection
@echo "***Testing Pure Javascript***"
@find test/integration -name "*-tests.js" | $(node-command)

test-binary: test-connection-binary
@echo "***Testing Pure Javascript (binary)***"
@find test/integration -name "*-tests.js" | $(node-command) --binary true
258 changes: 258 additions & 0 deletions lib/binaryParsers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
var parseBits = function(data, bits, offset, invert, callback) {
offset = offset || 0;
invert = invert || false;
callback = callback || function(lastValue, newValue, bits) { return (lastValue * Math.pow(2, bits)) + newValue; };
var offsetBytes = offset >> 3;

var inv = function(value) {
if (invert) {
return ~value & 0xff;
}

return value;
};

// read first (maybe partial) byte
var mask = 0xff;
var firstBits = 8 - (offset % 8);
if (bits < firstBits) {
mask = (0xff << (8 - bits)) & 0xff;
firstBits = bits;
}

if (offset) {
mask = mask >> (offset % 8);
}

var result = 0;
if ((offset % 8) + bits >= 8) {
result = callback(0, inv(data[offsetBytes]) & mask, firstBits);
}

// read bytes
var bytes = (bits + offset) >> 3;
for (var i = offsetBytes + 1; i < bytes; i++) {
result = callback(result, inv(data[i]), 8);
}

// bits to read, that are not a complete byte
var lastBits = (bits + offset) % 8;
if (lastBits > 0) {
result = callback(result, inv(data[bytes]) >> (8 - lastBits), lastBits);
}

return result;
};

var parseFloatFromBits = function(data, precisionBits, exponentBits) {
var bias = Math.pow(2, exponentBits - 1) - 1;
var sign = parseBits(data, 1);
var exponent = parseBits(data, exponentBits, 1);

if (exponent === 0)
return 0;

// parse mantissa
var precisionBitsCounter = 1;
var parsePrecisionBits = function(lastValue, newValue, bits) {
if (lastValue === 0) {
lastValue = 1;
}

for (var i = 1; i <= bits; i++) {
precisionBitsCounter /= 2;
if ((newValue & (0x1 << (bits - i))) > 0) {
lastValue += precisionBitsCounter;
}
}

return lastValue;
};

var mantissa = parseBits(data, precisionBits, exponentBits + 1, false, parsePrecisionBits);

// special cases
if (exponent == (Math.pow(2, exponentBits + 1) - 1)) {
if (mantissa === 0) {
return (sign === 0) ? Infinity : -Infinity;
}

return NaN;
}

// normale number
return ((sign === 0) ? 1 : -1) * Math.pow(2, exponent - bias) * mantissa;
};

var parseBool = function(value) {
return (parseBits(value, 8) == 1);
};

var parseInt16 = function(value) {
if (parseBits(value, 1) == 1) {
return -1 * (parseBits(value, 15, 1, true) + 1);
}

return parseBits(value, 15, 1);
};

var parseInt32 = function(value) {
if (parseBits(value, 1) == 1) {
return -1 * (parseBits(value, 31, 1, true) + 1);
}

return parseBits(value, 31, 1);
};

var parseInt64 = function(value) {
if (parseBits(value, 1) == 1) {
return -1 * (parseBits(value, 63, 1, true) + 1);
}

return parseBits(value, 63, 1);
};

var parseFloat32 = function(value) {
return parseFloatFromBits(value, 23, 8);
};

var parseFloat64 = function(value) {
return parseFloatFromBits(value, 52, 11);
};

var parseNumeric = function(value) {
var sign = parseBits(value, 16, 32);
if (sign == 0xc000) {
return NaN;
}

var weight = Math.pow(10000, parseBits(value, 16, 16));
var result = 0;

var digits = [];
var ndigits = parseBits(value, 16);
for (var i = 0; i < ndigits; i++) {
result += parseBits(value, 16, 64 + (16 * i)) * weight;
weight /= 10000;
}

var scale = Math.pow(10, parseBits(value, 16, 48));
return ((sign === 0) ? 1 : -1) * Math.round(result * scale) / scale;
};

var parseDate = function(value) {
var sign = parseBits(value, 1);
var rawValue = parseBits(value, 63, 1);

// discard usecs and shift from 2000 to 1970
var result = new Date((((sign === 0) ? 1 : -1) * rawValue / 1000) + 946684800000);

// add microseconds to the date
result.usec = rawValue % 1000;
result.getMicroSeconds = function() {
return this.usec;
};
result.setMicroSeconds = function(value) {
this.usec = value;
};
result.getUTCMicroSeconds = function() {
return this.usec;
};

return result;
};

var parseArray = function(value) {
var dim = parseBits(value, 32);

var flags = parseBits(value, 32, 32);
var elementType = parseBits(value, 32, 64);

var offset = 96;
var dims = [];
for (var i = 0; i < dim; i++) {
// parse dimension
dims[i] = parseBits(value, 32, offset);
offset += 32;

// ignore lower bounds
offset += 32;
}

var parseElement = function(elementType) {
// parse content length
var length = parseBits(value, 32, offset);
offset += 32;

// parse null values
if (length == 0xffffffff) {
return null;
}

if ((elementType == 0x17) || (elementType == 0x14)) {
// int/bigint
var result = parseBits(value, length * 8, offset);
offset += length * 8;
return result;
}
else if (elementType == 0x19) {
// string
var result = value.toString(this.encoding, offset >> 3, (offset += (length << 3)) >> 3);
return result;
}
else {
console.log("ERROR: ElementType not implemented: " + elementType);
}
};

var parse = function(dimension, elementType) {
var array = [];

if (dimension.length > 1) {
var count = dimension.shift();
for (var i = 0; i < count; i++) {
array[i] = parse(dimension, elementType);
}
dimension.unshift(count);
}
else {
for (var i = 0; i < dimension[0]; i++) {
array[i] = parseElement(elementType);
}
}

return array;
};

return parse(dims, elementType);
};

var parseText = function(value) {
return value.toString('utf8');
};

var parseBool = function(value) {
return (parseBits(value, 8) > 0);
};

var init = function(register) {
register(20, parseInt64);
register(21, parseInt16);
register(23, parseInt32);
register(26, parseInt32);
register(1700, parseNumeric);
register(700, parseFloat32);
register(701, parseFloat64);
register(16, parseBool);
register(1114, parseDate);
register(1184, parseDate);
register(1007, parseArray);
register(1016, parseArray);
register(1008, parseArray);
register(1009, parseArray);
register(25, parseText);
};

module.exports = {
init: init
};
6 changes: 5 additions & 1 deletion lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var Client = function(config) {
this.connection = config.connection || new Connection({stream: config.stream});
this.queryQueue = [];
this.password = config.password || defaults.password;
this.binary = config.binary || defaults.binary;
this.encoding = 'utf8';
this.processID = null;
this.secretKey = null;
Expand Down Expand Up @@ -172,7 +173,10 @@ p._pulseQueryQueue = function() {

p.query = function(config, values, callback) {
//can take in strings or config objects
config = (config.text || config.name) ? config : { text: config };
config = (typeof(config) == 'string') ? { text: config } : config;
if (this.binary && !('binary' in config)) {
config.binary = true;
}

if(values) {
if(typeof values === 'function') {
Expand Down
16 changes: 14 additions & 2 deletions lib/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ p.bind = function(config, more) {
config = config || {};
config.portal = config.portal || '';
config.statement = config.statement || '';
config.binary = config.binary || false;
var values = config.values || [];
var len = values.length;
var buffer = this.writer
Expand All @@ -157,7 +158,14 @@ p.bind = function(config, more) {
buffer.addString(val);
}
}
buffer.addInt16(0); //no format codes, use text

if (config.binary) {
buffer.addInt16(1); // format codes to use binary
buffer.addInt16(1);
}
else {
buffer.addInt16(0); // format codes to use text
}
//0x42 = 'B'
this._send(0x42, more);
};
Expand Down Expand Up @@ -383,7 +391,7 @@ p.parseD = function(msg) {
var fields = [];
for(var i = 0; i < fieldCount; i++) {
var length = this.parseInt32();
fields[i] = (length === -1 ? null : this.readString(length))
fields[i] = (length === -1 ? null : this.readBytes(length))
};
msg.fieldCount = fieldCount;
msg.fields = fields;
Expand Down Expand Up @@ -466,6 +474,10 @@ p.readString = function(length) {
return this.buffer.toString(this.encoding, this.offset, (this.offset += length));
};

p.readBytes = function(length) {
return this.buffer.slice(this.offset, this.offset += length);
};

p.parseCString = function() {
var start = this.offset;
while(this.buffer[this.offset++]) { };
Expand Down
4 changes: 3 additions & 1 deletion lib/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ module.exports = {
//0 will disable connection pooling
poolSize: 10,
//duration of node-pool timeout
poolIdleTimeout: 30000
poolIdleTimeout: 30000,
// binary result mode
binary: false
}
2 changes: 1 addition & 1 deletion lib/native/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ var mapRowData = function(row) {
var result = {};
for(var i = 0, len = row.length; i < len; i++) {
var item = row[i];
result[item.name] = item.value == null ? null : types.getStringTypeParser(item.type)(item.value);
result[item.name] = item.value == null ? null : types.getTypeParser(item.type, 'text')(item.value);
}
return result;
}
Expand Down
Loading