Bug #60313 bug in com.mysql.jdbc.ResultSetRow.getTimestampFast
Submitted: 3 Mar 2011 13:00 Modified: 16 Dec 2011 8:37
Reporter: Sergei Golubchik Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / J Severity:S3 (Non-critical)
Version:5.1.11 OS:Any
Assigned to: CPU Architecture:Any

[3 Mar 2011 13:00] Sergei Golubchik
Description:
the code is as follows:

src/com/mysql/jdbc/ResultSetRow.java:
1106      for (int i = 0; i < length; i++) {
1107        if (timestampAsBytes[offset + i] == '.') {
1108          decimalIndex = i;
1109        }
1110      }
1111 
1112      if (decimalIndex != -1) {
1113        if ((decimalIndex + 2) <= length) {
1114          nanos = StringUtils.getInt(
1115              timestampAsBytes, decimalIndex + 1,
1116              offset + length);

as you can see, nanos value starts at timestampAsBytes[offset + decimalIndex].
But StringUtils.getInt() is called with the starting offset decimalIndex, not offset + decimalIndex.

How to repeat:
package com.test.test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class TestTimeStamps {
   public static void main(String[] args) {
        Connection conn;
        try {
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            String url = "jdbc:mysql://127.0.0.1:3308/test";
            String userName = "test";
            String userPassword = "";
            conn = DriverManager.getConnection(url, userName, userPassword);
            Statement stmt = conn.createStatement();
            stmt.executeQuery("select repeat('Z', 3000), now() + interval 1 microsecond");
            ResultSet rs = stmt.getResultSet();
            try {
                while (rs.next()) {
                    for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
                        System.out.print(rs.getObject(i)+", ");
                    }
                    System.out.println("");
                }
            }
            finally {
                rs.close();
            }
        } catch (Exception e) {
            System.out.println("Exception: "+e);
            e.printStackTrace();
        }
    }
}
[3 Mar 2011 16:43] Tonci Grgin
Hey Serg!

I believe I know exactly what you're talking about. Will check.
[21 Mar 2011 10:02] Philip Stoev
Another example involving TIME and getTime();

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

class b1 {
   public static void main(String[] args) {
        Connection conn;
        try {
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            String url = "jdbc:mysql://127.0.0.1:3306/test";
            String userName = "test";
            String userPassword = "";
            conn = DriverManager.getConnection(url, userName, userPassword);
            Statement stmt = conn.createStatement();
            stmt.executeQuery("SELECT time(now() + interval 1 microsecond);");
            ResultSet rs = stmt.getResultSet();
            rs.next();
            rs.getTime(1);
            rs.close();
        } catch (Exception e) {
            System.out.println("Exception: "+e);
            e.printStackTrace();
        }
    }
}
[21 Mar 2011 11:04] Tonci Grgin
Thank you Philip, let me see if it works with my patch.
[21 Mar 2011 14:51] Tonci Grgin
Committed revision 1050.
[22 Mar 2011 9:23] Philip Stoev
Tonci, I am afraid your fix does not work for my test case. Time values with milliseconds can be anywhere from 10 to 15 characters long and the way the loop in ResultSetRow.java is structured those cases are not handled.
[22 Mar 2011 9:46] Tonci Grgin
Philip, I tested with your code and found no problems... I also do not see any size limitations for nanos:
@@ -1111,10 +1111,10 @@
	if (decimalIndex != -1) {
		if ((decimalIndex + 2) <= length) {
			nanos = StringUtils.getInt(
-				timestampAsBytes, decimalIndex + 1,
+				timestampAsBytes, offset + decimalIndex + 1,
 				offset + length);
 									
-			int numDigits = (offset + length) - (decimalIndex + 1);
+			int numDigits = (length) - (decimalIndex + 1); 
 									
 			if (numDigits < 9) {
 				int factor = (int)(Math.pow(10, 9 - numDigits));
[16 Dec 2011 8:37] Philip Olson
5.1.16 changelog entry:

ResultSetRow.getTimestampFast and getTime had invalid offsets.