-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Changes parseLengthCodedNumber to parse big numbers #382
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
Conversation
This returns them as strings instead of numbers
Not yet to merge, just for people to comment and test locally. Documentation and tests are needed later. |
works with the example from #380 |
If you decide at the end, the value is already overflown, must be tested around line 162: } else if (byte === 254) {
length = 8;
if (this._buffer[this._offset + 7] === 1) {
//HELP Big Number!
}
} else { |
if (BigNumber) { | ||
value = value.plus((new BigNumber(256)).pow(bytesRead).times(byte)); | ||
} else if (bytesRead == 7 && byte == 1) { | ||
BigNumber = require("bignumber.js"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will make blocking FS calls, even if the module has been cached before. If you want to lazy-load this, you'll need to have a top-level variable that you use as an in-module cache.
edit: never mind, I see you're doing that. ignore me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, @felixge you were right. That variable is within the Parser.prototype.parseLengthCodedNumber
function and should probably be moved out to the module level, otherwise require
will be called each time a length coded number is parsed that is too large.
From line 156: var length, bytesRead;
var BigNumber = false;
var value = 0;
if (byte === 252) {
length = 2;
} else if (byte === 253) {
length = 3;
} else if (byte === 254) {
length = 8;
if (this._buffer[this._offset + 7] === 1) {
BigNumber = require("bignumber.js");
value = new BigNumber(0);
}
} else {
throw new Error('parseLengthCodedNumber: Unexpected first byte: ' + byte);
}
for (bytesRead = 0; bytesRead < length; bytesRead++) {
byte = this._buffer[this._offset++];
// overflow
if (BigNumber) {
value = value.plus((new BigNumber(256)).pow(bytesRead).times(byte));
} else {
value += Math.pow(256, bytesRead) * byte;
}
}
return (BigNumber ? value.toString() : value); Works with example from #380 |
@kai-koch: I like your code, I just started the pull request before optimizing because I wanted to discuss the use of that dependency. I tested others and they all seemed bloated or faulty. @felixge : you're saying the dependency should be on top of the file? I just load it if a big number is found, otherwise never require it.. and it should cache for future requires.. no? |
I found something strange: TRUNCATE t;
ALTER TABLE `t` AUTO_INCREMENT =72057594037927935 the first run of my example returns an unprecise JavaScript number the second one the correct number.
In the database the entries start correctly at 72057594037927935 |
I'm having the same bug on OSX, I'm going to take a closer look. |
I think that the very first overflow number does not have the byte = 1 on index 7, it has byte = 255 on index 0-6. |
I think |
OK, I'm just finishing bug fix and I'll update asap. |
…754 precision limit
My Windows calculator says this:
But I don't know how it is encode in the buffer node-mysql is using. |
It's exactly that, but reversed (little endian). But we're still doing it wrong. The upper limit must be 2^53, which is: 9007199254740992. |
Works for both numbers now. :-) |
@felixge : any objections? :) |
Question: Is that a signed or unsigned Big Int encoded in the Buffer? EDIT: Duh, unsigned of course, since it is defined by the table defenition |
We still got a problem: (9223372036854775807 is the max. value for signed 64-Interger) |
@@ -154,30 +155,35 @@ Parser.prototype.parseLengthCodedNumber = function() { | |||
} | |||
|
|||
var length; | |||
var big_number = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rest of the code would use bigNumber
, not big_number
.
Well, I dislike switching the returned type from Number to String on >= 53 bit, this could certainly introduce subtle bugs in clients. That being said, I can't think of a better solution either (other than switching to a more enlightened language like Go, which I did recently : ). Anyway, patch LGTM otherwise. |
We could make an option to enable (or disable) this. Just need to know what is best by default. |
I would favor the current behavior as the default. |
yep. better to use current behavior as the default. |
…er number precision (if big number support is off)
Done some changes. What other behavior should be changed? |
Yes.
? |
I'm waiting while it will be fixed in the npm repository |
…t cast to number if string value is over precision limit
For LONGLONG and other number columns, if supportBigNumbers option is enabled, won't be converted to Number if over precision limit. |
I got the bignumber branch, it still has problems with numbers greater than '9223372036854775807' if (this._buffer[this._offset + 6].toString(2).length > 5 || this._buffer[this._offset + 7] == 1) { Is wrong.
This way you only detect big numbers that are smaller than 2^58. So for unsigned 64-bit Integers line 156 must be: if (this._buffer[this._offset + 6].toString(2).length > 5 || this._buffer[this._offset + 7]) { Any bit set in byte with index 7 means BigNumber. Which brings me to signed 64-Integers for these the line must look something like this: if (this._buffer[this._offset + 6].toString(2).length > 5 ||
(this._buffer[this._offset + 7] > 0 && this._buffer[this._offset + 7] < 128 ) ) { Because: [edit: linenumber] |
Another thing: this._buffer[this._offset + 6] > 31 is slightly faster than this._buffer[this._offset + 6].toString(2).length > 5 |
While I am nagging, another suggestion since you are working on that part of the code anyway: |
I'm trusting on your code, I don't have time now to think about it :P I'm pulling the code now. |
You better should have. ;) Use the line for unsigend Integers, please: if (this._buffer[this._offset + 6] > 31 || this._buffer[this._offset + 7]) { |
This will work until someone works with signed 64-Bit greater than '9223372036854775807' or less than 0. |
Changes parseLengthCodedNumber to parse big numbers
Add _binary prefix when interpolating []byte data.
The idea is to parse numbers over the JS precision using an external dependency and return the number as string.