From f867557ea287b9353cfb640412b1359219802501 Mon Sep 17 00:00:00 2001 From: Andrey Sidorov Date: Wed, 11 Jun 2014 10:49:36 +1000 Subject: [PATCH] Bigints support #108 --- Changelog.md | 9 ++- lib/commands/query.js | 4 +- lib/packets/packet.js | 56 ++++++++++++------- lib/packets/resultset_header.js | 6 +- package.json | 1 + .../connection/test-insert-bigint.js | 36 ++++++++++++ 6 files changed, 85 insertions(+), 27 deletions(-) create mode 100644 test/integration/connection/test-insert-bigint.js diff --git a/Changelog.md b/Changelog.md index e12c660425..6baebce7b6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,7 +5,14 @@ Backlog: HEAD -0.12.1 0 30/04/2014 +0.12.2 - 11/07/2014 + + - output milliseconds in date type #107 + - deserialise length coded int with > 24 bit numbers + to js int / float (and not throw "Bignts not supported") #108 + - support for Bigint numbers in insertId + +0.12.1 - 30/04/2014 - 'dateStrings' connection option support #99 - use anonymous function for packet routing instead diff --git a/lib/commands/query.js b/lib/commands/query.js index 1fae7b211e..4b79b7d967 100644 --- a/lib/commands/query.js +++ b/lib/commands/query.js @@ -49,8 +49,8 @@ Query.prototype.done = function() { return null; }; -Query.prototype.resultsetHeader = function(packet) { - var rs = new Packets.ResultSetHeader(packet); +Query.prototype.resultsetHeader = function(packet, connection) { + var rs = new Packets.ResultSetHeader(packet, connection.config.bigNumberStrings); this._fieldCount = rs.fieldCount; if (this._fieldCount === 0) { diff --git a/lib/packets/packet.js b/lib/packets/packet.js index f8d5ac682d..83140f0d3f 100644 --- a/lib/packets/packet.js +++ b/lib/packets/packet.js @@ -1,6 +1,8 @@ //var BigNumber = require("bignumber.js"); var ErrorCodeToName = require('../constants/errors').codeToName; +var bn = require('bn.js'); + function Packet(id, buffer, start, end) { this.sequenceId = id; @@ -102,7 +104,9 @@ Packet.prototype.eofWarningCount = function() { return this.buffer.readInt16LE(this.offset + 1); }; -Packet.prototype.readLengthCodedNumber = function() { +Packet.prototype.readLengthCodedNumber = function(bigNumberStrings) { + var word0, word1; + var res; var byte1 = this.readInt8(); if (byte1 < 0xfb) return byte1; @@ -113,8 +117,17 @@ Packet.prototype.readLengthCodedNumber = function() { return this.readInt8() + (this.readInt8() << 8) + (this.readInt8() << 16); } if (byte1 == 0xfe) { - console.trace(); - //throw "Implement 8bytes BigNumber"; + // TODO: check version + // Up to MySQL 3.22, 0xfe was followed by a 4-byte integer. + word0 = this.readInt32(); + word1 = this.readInt32(); + if (word1 === 0) + return word0; // don't convert to float if possible + if (word1 < 2097152) // max exact float point int, 2^52 / 2^32 + return word1*0x100000000 + word0; + + res = (new bn(word1)).ishln(32).iaddn(word0); + return bigNumberStrings ? res.toString() : res; } if (byte1 == 0xfb) return null; @@ -137,7 +150,7 @@ Packet.prototype.readDouble = function() { Packet.prototype.readBuffer = function(len) { if (typeof len == 'undefined') - len = this.end - this.offset + len = this.end - this.offset; this.offset += len; return this.buffer.slice(this.offset - len, this.offset); }; @@ -197,7 +210,7 @@ Packet.prototype.readDateTimeString = function() { y = this.readInt16(); m = this.readInt8(); d = this.readInt8(); - str = [leftPad(4, y), leftPad(2, m), leftPad(2, d)].join('-') + str = [leftPad(4, y), leftPad(2, m), leftPad(2, d)].join('-'); } if (length > 6) { H = this.readInt8(); @@ -316,33 +329,34 @@ Packet.prototype.parseGeometryValue = function() { } function parseGeometry() { + var x, y, i, j, numPoints, line; var result = null; var byteOrder = buffer.readUInt8(offset); offset += 1; var wkbType = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4; switch(wkbType) { case 1: // WKBPoint - var x = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8; - var y = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8; + x = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8; + y = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8; result = {x: x, y: y}; break; case 2: // WKBLineString - var numPoints = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4; + numPoints = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4; result = []; - for(var i=numPoints;i>0;i--) { - var x = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8; - var y = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8; + for(i=numPoints;i>0;i--) { + x = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8; + y = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8; result.push({x: x, y: y}); } break; case 3: // WKBPolygon var numRings = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4; result = []; - for(var i=numRings;i>0;i--) { - var numPoints = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4; - var line = []; - for(var j=numPoints;j>0;j--) { - var x = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8; - var y = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8; + for(i=numRings;i>0;i--) { + numPoints = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4; + line = []; + for(j=numPoints;j>0;j--) { + x = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8; + y = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8; line.push({x: x, y: y}); } result.push(line); @@ -353,8 +367,8 @@ Packet.prototype.parseGeometryValue = function() { case 6: // WKBMultiPolygon case 7: // WKBGeometryCollection var num = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4; - var result = []; - for(var i=num;i>0;i--) { + result = []; + for(i=num;i>0;i--) { result.push(parseGeometry()); } break; @@ -379,14 +393,14 @@ Packet.prototype.parseDate = function() { this.offset++; // - var d = this.parseInt(2); return new Date(y, m-1, d); -} +}; Packet.prototype.parseDateTime = function() { var str = this.readLengthCodedString(); if (str === null) return null; return new Date(str); -} +}; // TODO: handle E notation diff --git a/lib/packets/resultset_header.js b/lib/packets/resultset_header.js index 0e588fe5ae..c3b2c75ce5 100644 --- a/lib/packets/resultset_header.js +++ b/lib/packets/resultset_header.js @@ -2,14 +2,14 @@ var Packet = require('../packets/packet'); -function ResultSetHeader(packet) +function ResultSetHeader(packet, bigNumberStrings) { if (packet.buffer[packet.offset] !== 0) { this.fieldCount = packet.readLengthCodedNumber(); } else { this.fieldCount = packet.readInt8(); // skip OK byte - this.affectedRows = packet.readLengthCodedNumber(); - this.insertId = packet.readLengthCodedNumber(); + this.affectedRows = packet.readLengthCodedNumber(bigNumberStrings); + this.insertId = packet.readLengthCodedNumber(bigNumberStrings); this.serverStatus = packet.readInt16(); this.warningStatus = packet.readInt16(); } diff --git a/package.json b/package.json index 9d041a4354..4632370e64 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "author": "Andrey Sidorov ", "license": "MIT", "dependencies": { + "bn.js": "^0.11.7", "fastqueue": "~0.1.0" }, "devDependencies": { diff --git a/test/integration/connection/test-insert-bigint.js b/test/integration/connection/test-insert-bigint.js new file mode 100644 index 0000000000..4d73e94ce3 --- /dev/null +++ b/test/integration/connection/test-insert-bigint.js @@ -0,0 +1,36 @@ +var common = require('../../common'); +var connection = common.createConnection({ bigNumberString: true }); +var assert = require('assert'); +var bn = require('bn.js'); + +var table = 'insert_test'; +connection.query([ + 'CREATE TEMPORARY TABLE `bigs` (', + '`id` bigint NOT NULL AUTO_INCREMENT,', + '`title` varchar(255),', + 'PRIMARY KEY (`id`)', + ') ENGINE=InnoDB DEFAULT CHARSET=utf8' +].join('\n')); + +var result, result2; +connection.query("INSERT INTO bigs SET title='test', id=123"); +connection.query("INSERT INTO bigs SET title='test1'", function(err, result) { + if (err) throw err; + assert.strictEqual(result.insertId, 124); + // > 24 bits + connection.query("INSERT INTO bigs SET title='test', id=123456789"); + connection.query("INSERT INTO bigs SET title='test2'", function(err, result) { + assert.strictEqual(result.insertId, 123456790); + // big int + connection.query("INSERT INTO bigs SET title='test', id=9007199254740992"); + connection.query("INSERT INTO bigs SET title='test3'", function(err, result) { + assert.strictEqual((new bn("9007199254740993")).cmp(result.insertId), 0); + connection.query("INSERT INTO bigs SET title='test', id=90071992547409924"); + connection.config.bigNumberStrings = true; + connection.query("INSERT INTO bigs SET title='test4'", function(err, result) { + assert.strictEqual(result.insertId, "90071992547409925"); + connection.end(); + }); + }); + }); +});