Skip to content
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

Merged
merged 9 commits into from
Apr 4, 2016

Conversation

harshit-gangal
Copy link
Member

Review on Reviewable

@googlebot
Copy link

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. I signed it!) and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If you signed the CLA as a corporation, please let us know the company's name.

@enisoc enisoc self-assigned this Feb 10, 2016
this.value = ByteString.copyFromUtf8(((boolean)value) ? "1" : "0");
} else if (value instanceof BigDecimal) {
// BigDecimal
this.type = Query.Type.FLOAT64;
Copy link
Member

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().

Copy link
Member Author

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.

Copy link
Member

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().

Copy link
Member Author

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

@sougou
Copy link
Contributor

sougou commented Feb 10, 2016

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.

@enisoc
Copy link
Member

enisoc commented Feb 10, 2016

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.

@enisoc
Copy link
Member

enisoc commented Feb 11, 2016

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());
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That can be done.

@enisoc
Copy link
Member

enisoc commented Feb 17, 2016

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 toString(). I've changed it to toPlainString() to show that the test passes. I kept the rounding when scale>30, but I don't think we should unnecessarily setScale(30) if the existing scale is small enough.

Lastly, regarding new BigDecimal(0.23), the "error" is not introduced by toPlainString() as you implied. The error is in using the BigDecimal(double) constructor at all. That constructor is known to produce unexpected results:

https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#BigDecimal(double)

If you instead use BigDecimal.valueOf(double), the results will be as expected. I've made this change as well and added a test case to prove it works.

@harshit-gangal
Copy link
Member Author

I understand your point now.

On Thu, Feb 18, 2016 at 3:48 AM, Anthony Yeh notifications@github.com
wrote:

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 toString(). I've changed it to toPlainString() to show
that the test passes. I kept the rounding when scale>30, but I don't think
we should unnecessarily setScale(30) if the existing scale is small enough.

Lastly, regarding new BigDecimal(0.23), the "error" is not introduced by
toPlainString() as you implied. The error is in using the
BigDecimal(double) constructor at all. That constructor is known to
produce unexpected results:

https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#BigDecimal(double)

If you instead use BigDecimal.valueOf(double), the results will be as
expected. I've made this change as well and added a test case to prove it
works.


Reply to this email directly or view it on GitHub
#1495 (comment).

@harshit-gangal
Copy link
Member Author

@enisoc
Copy link
Member

enisoc commented Mar 14, 2016

Reviewed 2 of 2 files at r4.
Review status: all files reviewed at latest revision, 2 unresolved discussions.


java/client/src/main/java/com/youtube/vitess/client/Proto.java, line 217 [r4] (raw file):
Is there a benefit to using String.valueOf(1) instead of just "1"?


Comments from the review on Reviewable.io

@harshit-gangal
Copy link
Member Author

java/client/src/main/java/com/youtube/vitess/client/Proto.java, line 217 [r4] (raw file):
No Particular reason. Change Done.


Comments from the review on Reviewable.io

@enisoc
Copy link
Member

enisoc commented Mar 14, 2016

:lgtm: Looks ready to merge, once CLA is approved.


Reviewed 1 of 1 files at r5.
Review status: all files reviewed at latest revision, 1 unresolved discussion, some commit checks broke.


Comments from the review on Reviewable.io

@googlebot
Copy link

CLAs look good, thanks!

@googlebot googlebot added cla: yes and removed cla: no labels Apr 3, 2016
@harshit-gangal
Copy link
Member Author

Needs to approve pull request here
https://pullapprove.com/youtube/vitess/pull-request/1495/

@enisoc enisoc merged commit b423c73 into master Apr 4, 2016
@enisoc enisoc deleted the additional_bind_fields branch April 4, 2016 22:10
dbussink pushed a commit that referenced this pull request Jan 30, 2023
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants