-
Notifications
You must be signed in to change notification settings - Fork 426
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
PreparedStatement.setTimestamp(index, timestamp, calendar) has datatype conversion issues. #443
Comments
This appears to be a result of sending the Timestamp parameter value as exec sp_executesql N'select count(*) from #TEST_TIMESTAMP_CAL where TIMESTAMP_COL_NONE = @P0 ',N'@P0 datetime2','2007-01-01 01:01:01.9990000' We can see the same behaviour with a stored procedure that tries to use a The following procedure returns a count of CREATE PROCEDURE issue_443
@p1 DATETIME2 = '2007-01-01 01:01:01.999'
AS
BEGIN
SET NOCOUNT ON;
CREATE TABLE #TEST_TIMESTAMP_CAL (ID INTEGER NOT NULL, TIMESTAMP_COL_NONE DATETIME NOT NULL);
INSERT INTO #TEST_TIMESTAMP_CAL (ID, TIMESTAMP_COL_NONE) VALUES (1, '2007-01-01 01:01:01.999');
SELECT COUNT(*) AS n FROM #TEST_TIMESTAMP_CAL WHERE TIMESTAMP_COL_NONE = @p1;
END ... but if we simply change the parameter type from ALTER PROCEDURE issue_443
@p1 DATETIME = '2007-01-01 01:01:01.999'
AS
... ... then it returns a count of |
So I've been thinking this through and I'm not sure if this is fixable. It appears to be a very unfortunate combination of rounding, driver parameter type selection and default casting/conversion rules. The drivers that work send the data as datetime. Modifying the current driver to send datetime also works, but I'm reasonably certain that would break datetime2 columns. Here is my current understanding of how this works:
So is this a driver bug? Can the driver intelligently choose between datetime and datetime2? Is the default conversion rule for |
@gs-rezaem - I think you've nailed it. I had forgotten about The good news is that it is fixable if the driver responds to The bad news is that this extra step (and server round-trip) is something that the mssql-jdbc team has been working hard on trying to optimize out of the process, primarily for the case when A user-side workaround would be to specify the parameters for I seem to recall someone ( @TobiasSQL ?) mentioning an option to turn off the optimization(s) and let the driver work with |
@gordthompson it'd be great for the driver to fix this, because it's much harder to fix at the app/ORM layer. One of the core promises of JDBC is taking care of DB specific types. An analogous example is the mapping to String: set/getString ought to work regardless of whether the column is defined as char, varchar, nvarchar, unichar, clob, text or any other DB specific type. I didn't really want to go into workarounds, because they all cause other problems. For example, an ORM could issue the SQL as |
I tried the driver parameter |
Perhaps. I've been following some similar discussions regarding pyodbc (e.g., here and here) so I may have assumed JDBC behaviour based on what I understand about ODBC behaviour. Now that I think about it, when they say "enablePrepareOnFirstPreparedStatementCall" they may actually mean that the first JDBC |
That is correct @gordthompson . Setting |
@gordthompson , I am probably missing something but isn't the analog of ODBC's If you use this API to determine the type that should be a reasonable solution, no? |
@TobiasSQL - Good suggestion. exec sp_executesql N'exec sp_describe_undeclared_parameters @P0 ',N'@P0 nvarchar(4000)',N'select count(*) from TEST_TIMESTAMP_CAL where TIMESTAMP_COL_NONE = @P0 ' to the server, and if we interrogate the What seems to be missing is the way for the JDBC driver to know that too, so if we use |
@peterbae thanks for having a look at this. I don't believe this is trivial to fix, but thanks to the conversation here, I'd like to propose a fix: Calling
Pros: Fixes the issue. No extra database hops if What do you think of this? If the above has a good chance being merged, I'll see if I can code it up. Can you point me to an existing test case that I can use as a template for this? |
Hi @gs-rezaem, thanks for the contribution. I had thought of a very similar solution to yours for this problem, but I couldn't get to the implementation as I was busy with focusing on other priorities. Please feel free to implement this and create a PR - I have a couple of comments as well:
As for the existing test case, the tests under TVP and BulkCopy test all datatypes, so you can run those to verify your fixes. For example: https://github.com/Microsoft/mssql-jdbc/blob/master/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPAllTypes.java#L144 |
@gs-rezaem, wondering if you're still planning on submitting a fix? |
Sorry, I was out with a cold and just getting back into this. I expect this will take me another week to deliver. |
@gs-rezaem, glad you're feeling better 😄 let us know if you need any pointers or run into any issues. Thanks again for your contributions. |
I had a little trouble with the test cases. I can't seem to find a test case that does a select statement with a parameterized prepared statement. Let me know if I missed something. So here is the plan:
|
As I'm digging into this, I've realized that this bug has a larger surface area than I had originally thought. Specifically, the
I'm clarifying Shout now if that's not consistent with how you want this done 😄. |
I don't think we need to modify the Types.java file - we have DATETIME and DATETIME2 under SSType, so we can use those instead. I think we can solve this problem simply by modifying the Parameter::setTypeDefinition method for TIMESTAMP and DATETIME switch cases, where we would use a cached flag (that gets set whenever the user calls setTimeStamp, setDateTime or setObject), and set param.typeDefinition to DATETIME2 only if we're using Katmai servers and the target column on the SQL Server's end is DATETIME2. Please let me know if you need more help or clarification. 😄 |
The flag solution was what I had originally thought about. But as I'm looking at the code, I think it's both more practical and consistent to go with they type solution. Practical:
Consistency:
Edit: clarification on that last point. I'm left wondering: At the end of the day, it's your choice. Just let me know what you like. |
Another way I look at the issue is this: how did this happen? What was the root cause? |
I'm not sure about the reasoning behind the approach we took (to take advantage of the increased precision with DATETIME2?), but I like your approach as well. Please go ahead with it 👍 |
So I've run into a blocker, possibly another bug #493. I have no idea what to do there. Once that's fixed I'll continue here. |
@peterbae any thoughts as to how to proceed here or on #493?
(there is a 4th possibility CHAR. by always encoding as CHAR, the server can do the parsing/casting correctly without the need for a second metadata call). (aside, @ajlam, "waiting for response" is probably the wrong label on this) |
Hi @gs-rezaem, I think it would be best for us to wait until #493 is resolved, then continue with the implementation of this issue. We will keep you posted. |
Hi @gs-rezaem, it seems like #493 might take some time to be implemented - do you think it'll be possible for you to work a fix for this issue without the encrypted connections case? |
@peterbae The only solution I can think of is outlined in my last comment. This has been percolating in the back of my mind for a while, and I'm actually warming up to that path forward, for one thing because users who don't have this issue can opt out of the extra DB hit. |
Hi guys, any news on this issue? This problem is killing us in production ever since we upgraded to SQLServer 2016 and switched from jTDS to this driver. Is there something I can do to solve the problem without having to wait for the official fix? Just for reference: I posted a detailed description of the problem on stackoverflow and linked back to this ticket from there: https://stackoverflow.com/questions/48422032/hibernate-jtds-mssql-jdbc-driver-problems-with-datetime-column-in-sql-server-2 |
@kimzeevaarders I don't believe there is a perfect way to fix this. If I were in your shoes, I would do this: Option 1: If you own the schema, change all your datetime columns to datetime2(3). This is a good way to move your application forward. |
@gs-rezaem thanks for the feedback. I am indeed owner of the schema and updated the column datatypes to DATETIME2. The problem is fixed now. However, could you please shed your light on potential problems such a modification could cause that I am maybe not aware of c,q, need further testing? |
@kimzeevaarders Just a clarification: DATETIME2 defaults to (7) which might cause other problems when mapping to Java's millisecond precision. Use (3) to avoid mapping issues and minimize the column size. |
@gs-rezaem I am aware of this thanks! |
@gs-rezaem - |
@gordthompson Yes, you're right. Fixed my comment. I emphasized using (3) because it maps well to Java time and uses the least amount of storage. |
Hi @pacham7 , We will look into this in one of the upcoming releases. Please stay tuned. |
I really wish something like this would be implemented. There's a Our problem is there are some performance reasons for our application which makes sense to stick with We could have the best of both worlds, if we could just specify how we want |
For those running into problems via jOOQ, @pacham7 has documented a jOOQ specific workaround here: jOOQ/jOOQ#11916 (comment) |
I filed #1590 to add this specific feature. |
Any proposed solution? |
hi @httpmurilo This, along with the enhancement request filed for this issue are in our backlog they will be triaged when we do planning for the next semester. Unfortunately we do not have resources to deal with anything other than the highest priority issues right now. |
#1687 resolves this by allowing users to specify the datatype to use for date/timestamp parameters. @gs-rezaem , please let us know if this fix (included in 12.2+) works for you. Closing issue, but can reopen if needed. |
Driver version or jar name
The test works correctly with the old version of driver (2.0.1803), but fails with 4.x and 6.x, including 6.2.1 and 6.3 preview.
SQL Server version
Microsoft SQL Server 2008 (SP4) - 10.0.6241.0 (X64)
Client operating system
Windows 7
Java/JVM version
Oracle JDK 1.8.0_xxx
Table schema
See the test code Stand alone test class
Problem description
See the test code here: Stand alone test class
Expected behavior and actual behavior
When the query is executed with a prepared statement, it should return the correct result.
Repro code
Stand alone test class
This test code works correctly with MSSQL/2.0 driver, MSSQL/jtds driver, 4 other commercial and 3 open source databases. It fails with 4.x and 6.x series drivers.
The text was updated successfully, but these errors were encountered: