-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Mapping for Short,Byte,Date,Time,Timestamp,Boolean,BigDecimal in Java Client #1495
Conversation
harshit-gangal
commented
Feb 10, 2016
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://cla.developers.google.com/ to sign. Once you've signed, please reply here (e.g.
|
this.value = ByteString.copyFromUtf8(((boolean)value) ? "1" : "0"); | ||
} else if (value instanceof BigDecimal) { | ||
// BigDecimal | ||
this.type = Query.Type.FLOAT64; |
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.
I think BigDecimal should map to Query.Type.DECIMAL. Also when converting to string, I think BigDecimal.toPlainString() should work better than using setScale().
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 problem with that is the precision can change.
Ex. BigDecimal(0.23), BigDecimal.toPlanString() will result in 0.2300000000000000099920072216264088638126850128173828125
In MySQL the maximum scale is 30.
So if a column is defined as my_col decimal(2,30) then this will result in wrong data in the database.
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.
I see, so you use setScale() to limit to 30. Is that really necessary? In my tests, MySQL will do the rounding for you already; it doesn't mind if the value is longer than will fit.
The reason I think we need toPlainString() instead of toString() is because toString() will revert to exponential notation if the exponent is < -6. That is, new BigDecimal("0.0000000002").toString()
will become "2E-10". That's a problem because MySQL will interpret "2E-10" as an approximate, floating point numeric literal, rather than as an exact numeric literal:
http://dev.mysql.com/doc/refman/5.6/en/precision-math-numbers.html
To make sure MySQL interprets it as an exact numeric literal, we need to send "0.0000000002", which we can get from toPlainString().
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.
In MySQL you can have a scale of atmost 30.
So, If one gives value of BigDecimal as 0.23 and I store is as toPlainString then the value saved in database will be "0.230000000000000009992007221626" (Scale 30) and not "0.23" so on retrieval, they will get wrong value
Shouldn't there be counterpart changes to the place where results are received? For things to be symmetrical, those values need to be converted back to the native types. |
We're already converting these types on the receiving side, although we are using Joda DateTime instead of java.sql.Date and friends. We should think about whether it makes sense to switch the low-level Row class to return java.sql.Date, instead of making the JDBC wrapper convert from Joda to java.sql types. |
I've proposed #1499 to fix up the receiving side to follow standard conventions better. |
} else if (value instanceof Timestamp) { | ||
// DateTime, TimeStamp | ||
this.type = Query.Type.TIMESTAMP; | ||
this.value = ByteString.copyFromUtf8(value.toString()); |
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.
I found out that toString() and valueOf() on java.sql.(Date|Time|Timestamp) all use the local machine's default timezone. It'll work out as long as users only ever create java.sql.Timestamp values via valueOf(). But what if they create one from a java.util.Date or new Timestamp(millis)
? They would have to adjust their epoch times to be intentionally wrong to offset for the local machine's default timezone.
Ideally we would do everything in UTC, but that means we can use neither toString() nor valueOf(). We would have to do the parsing and formatting ourselves. I'm looking into it as part of #1499 where I'm working on the receiving side.
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 is known to Application Developer that is why they use SimpleDateFormat to generate date/time/timestamp field in the timezone they want and then send as setString() or setTimestamp(Timestamp.valueOf()) in the preparedStatement.
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.
I see what you're saying. The default in JDBC when you don't specify a time zone, is to use the machine's configured time zone. Since we don't offer any way to specify time zone when sending bind variables as a map, we should follow the JDBC convention of using the machine's time zone.
FYI, in #1499 I'm adding support in Cursor for getting date/time values with a given Calendar. This should make implementing ResultSet time/date methods easier. There is also a new utility class in that PR called com.youtube.vitess.mysql.DateTime, which contains format methods that will also be useful when writing PreparedStatement setDate(..., Calendar) and so on.
I think to make that work, when implementing PreparedStatement, we'll need to send date/time bind vars to the low-level Vitess client as String, after formatting them with com.youtube.vitess.mysql.DateTime.
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.
That can be done.
Regarding BigDecimal, I think we're having trouble understanding each other's arguments. I've added a commit to this branch to show in code form what I'm proposing. Please see the new test case for BigDecimal that will fail if you use Lastly, regarding https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#BigDecimal(double) If you instead use |
I understand your point now. On Thu, Feb 18, 2016 at 3:48 AM, Anthony Yeh notifications@github.com
|
Review status: 0 of 2 files reviewed at latest revision, 3 unresolved discussions. java/client/src/main/java/com/youtube/vitess/client/Proto.java, line 224 [r1] (raw file): java/client/src/main/java/com/youtube/vitess/client/Proto.java, line 228 [r1] (raw file): java/client/src/main/java/com/youtube/vitess/client/Proto.java, line 231 [r1] (raw file): Comments from the review on Reviewable.io |
Reviewed 2 of 2 files at r4. java/client/src/main/java/com/youtube/vitess/client/Proto.java, line 217 [r4] (raw file): Comments from the review on Reviewable.io |
java/client/src/main/java/com/youtube/vitess/client/Proto.java, line 217 [r4] (raw file): Comments from the review on Reviewable.io |
Looks ready to merge, once CLA is approved. Reviewed 1 of 1 files at r5. Comments from the review on Reviewable.io |
CLAs look good, thanks! |
Needs to approve pull request here |
* feat: add failing parsing test and fix parser Signed-off-by: Manan Gupta <manan@planetscale.com> * test: add e2e tests for interval with math functions Signed-off-by: Manan Gupta <manan@planetscale.com> * test: explictly set the time-zone to prevent failures in CI Signed-off-by: Manan Gupta <manan@planetscale.com> Signed-off-by: Manan Gupta <manan@planetscale.com> Signed-off-by: Manan Gupta <manan@planetscale.com>