From: Date: March 21 2007 9:01pm Subject: Connector/J commit: r6358 - branches/branch_5_0/connector-j branches/branch_5_0/connector-j/src/com/mysql/jdbc branches/branch_5_0/connector-j/src/testsuite/regression trunk/connector-j trunk/connector-j/src/com/mysql/jdbc trunk/connector-j/src/testsuite/regression List-Archive: http://lists.mysql.com/commits/22533 X-Bug: 26789 Message-Id: <200703212001.l2LK1Upw027684@bk-internal.mysql.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modified: branches/branch_5_0/connector-j/CHANGES branches/branch_5_0/connector-j/src/com/mysql/jdbc/ResultSet.java branches/branch_5_0/connector-j/src/testsuite/regression/ResultSetRegressionTest.java trunk/connector-j/CHANGES trunk/connector-j/src/com/mysql/jdbc/ResultSet.java trunk/connector-j/src/testsuite/regression/ResultSetRegressionTest.java Log: Fixed BUG#26789 - fast date/time parsing doesn't take into account 00:00:00 as a legal value. Modified: branches/branch_5_0/connector-j/CHANGES =================================================================== --- branches/branch_5_0/connector-j/CHANGES 2007-03-16 21:37:55 UTC (rev 6357) +++ branches/branch_5_0/connector-j/CHANGES 2007-03-21 20:01:27 UTC (rev 6358) @@ -13,7 +13,7 @@ - Fixed BUG#25624 - Whitespace surrounding storage/size specifiers in stored procedure parameters declaration causes NumberFormatException to be thrown when calling stored procedure on JDK-1.5 or newer, as the Number - classes in JDK-1.5+ are whitespace intolerant. + classes in JDK-1.5+ are whitespace intolerant. - Fixed BUG#26173 - When useCursorFetch=true, sometimes server would return new, more exact metadata during the execution of the server-side prepared @@ -29,7 +29,10 @@ - Give better error message when "streaming" result sets, and the connection gets clobbered because of exceeding net_write_timeout on the server. (which is basically what the error message says too). - + + - Fixed BUG#26789 - fast date/time parsing doesn't take into + account 00:00:00 as a legal value. + 03-01-07 - Version 5.0.5 - Fixed BUG#23645 - Some collations/character sets reported as "unknown" Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/ResultSet.java =================================================================== --- branches/branch_5_0/connector-j/src/com/mysql/jdbc/ResultSet.java 2007-03-16 21:37:55 UTC (rev 6357) +++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/ResultSet.java 2007-03-21 20:01:27 UTC (rev 6358) @@ -2140,6 +2140,10 @@ return fastDateCreate(null, 1970, 1, 1); // Return EPOCH } else { if (stringVal.length() < 10) { + if (stringVal.length() == 8) { + return fastDateCreate(null, 1970, 1, 1); // Return EPOCH for TIME + } + throw SQLError.createSQLException(Messages.getString( "ResultSet.Bad_format_for_Date", new Object[] { stringVal, new Integer(columnIndex) }), @@ -2171,7 +2175,7 @@ } } - private final java.sql.Date getDateFromBytes(byte[] fromServer, + private final java.sql.Date getDateFromBytes(byte[] dateAsBytes, int columnIndex) throws SQLException { checkColumnBounds(columnIndex); @@ -2182,7 +2186,7 @@ try { this.wasNullFlag = false; - if (fromServer == null) { + if (dateAsBytes == null) { this.wasNullFlag = true; return null; @@ -2191,19 +2195,26 @@ boolean allZeroDate = true; - for (int i = 0; i < fromServer.length; i++) { - if (fromServer[i] != '0' && - fromServer[i] != ' ' && - fromServer[i] != ':' && - fromServer[i] != '-' && - fromServer[i] != '/') { + boolean onlyTimePresent = true; + + int length = dateAsBytes.length; + + for (int i = 0; i < length; i++) { + byte b = dateAsBytes[i]; + + if (b == ' ' || b == '-' || b == '/') { + onlyTimePresent = false; + } + + if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' + && b != '.') { allZeroDate = false; - + break; } } - if (allZeroDate) { + if (!onlyTimePresent && allZeroDate) { if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(this.connection.getZeroDateTimeBehavior())) { @@ -2212,7 +2223,7 @@ return null; } else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(this.connection.getZeroDateTimeBehavior())) { - throw SQLError.createSQLException("Value '" + new String(fromServer) + throw SQLError.createSQLException("Value '" + new String(dateAsBytes) + "' can not be represented as java.sql.Date", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } @@ -2223,21 +2234,21 @@ } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { // Convert from TIMESTAMP - switch (fromServer.length) { + switch (length) { case 21: case 19: { // java.sql.Timestamp format - year = StringUtils.getInt(fromServer, 0, 4); - month = StringUtils.getInt(fromServer, 5, 7); - day = StringUtils.getInt(fromServer, 8, 10); + year = StringUtils.getInt(dateAsBytes, 0, 4); + month = StringUtils.getInt(dateAsBytes, 5, 7); + day = StringUtils.getInt(dateAsBytes, 8, 10); return fastDateCreate(null, year, month, day); } case 14: case 8: { - year = StringUtils.getInt(fromServer, 0, 4); - month = StringUtils.getInt(fromServer, 4, 6); - day = StringUtils.getInt(fromServer, 6, 8); + year = StringUtils.getInt(dateAsBytes, 0, 4); + month = StringUtils.getInt(dateAsBytes, 4, 6); + day = StringUtils.getInt(dateAsBytes, 6, 8); return fastDateCreate(null, year, month, day); } @@ -2245,32 +2256,32 @@ case 12: case 10: case 6: { - year = StringUtils.getInt(fromServer, 0, 2); + year = StringUtils.getInt(dateAsBytes, 0, 2); if (year <= 69) { year = year + 100; } - month = StringUtils.getInt(fromServer, 2, 4); - day = StringUtils.getInt(fromServer, 4, 6); + month = StringUtils.getInt(dateAsBytes, 2, 4); + day = StringUtils.getInt(dateAsBytes, 4, 6); return fastDateCreate(null, year + 1900, month, day); } case 4: { - year = StringUtils.getInt(fromServer, 0, 4); + year = StringUtils.getInt(dateAsBytes, 0, 4); if (year <= 69) { year = year + 100; } - month = StringUtils.getInt(fromServer, 2, 4); + month = StringUtils.getInt(dateAsBytes, 2, 4); return fastDateCreate(null, year + 1900, month, 1); } case 2: { - year = StringUtils.getInt(fromServer, 0, 2); + year = StringUtils.getInt(dateAsBytes, 0, 2); if (year <= 69) { year = year + 100; @@ -2282,13 +2293,13 @@ default: throw SQLError.createSQLException(Messages.getString( "ResultSet.Bad_format_for_Date", new Object[] { - new String(fromServer), new Integer(columnIndex) }), + new String(dateAsBytes), new Integer(columnIndex) }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ } /* endswitch */ } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { - if (fromServer.length == 2 || fromServer.length == 1) { - year = StringUtils.getInt(fromServer); + if (length == 2 || length == 1) { + year = StringUtils.getInt(dateAsBytes); if (year <= 69) { year = year + 100; @@ -2296,27 +2307,31 @@ year += 1900; } else { - year = StringUtils.getInt(fromServer, 0, 4); + year = StringUtils.getInt(dateAsBytes, 0, 4); } return fastDateCreate(null, year, 1, 1); } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIME) { return fastDateCreate(null, 1970, 1, 1); // Return EPOCH } else { - if (fromServer.length < 10) { + if (length < 10) { + if (length == 8) { + return fastDateCreate(null, 1970, 1, 1); // Return EPOCH for TIME + } + throw SQLError.createSQLException(Messages.getString( "ResultSet.Bad_format_for_Date", new Object[] { - new String(fromServer), new Integer(columnIndex) }), + new String(dateAsBytes), new Integer(columnIndex) }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ } - if (fromServer.length != 18) { - year = StringUtils.getInt(fromServer, 0, 4); - month = StringUtils.getInt(fromServer, 5, 7); - day = StringUtils.getInt(fromServer, 8, 10); + if (length != 18) { + year = StringUtils.getInt(dateAsBytes, 0, 4); + month = StringUtils.getInt(dateAsBytes, 5, 7); + day = StringUtils.getInt(dateAsBytes, 8, 10); } else { // JDK-1.3 timestamp format, not real easy to parse positionally :p - StringTokenizer st = new StringTokenizer(new String(fromServer), "- "); + StringTokenizer st = new StringTokenizer(new String(dateAsBytes), "- "); year = Integer.parseInt(st.nextToken()); month = Integer.parseInt(st.nextToken()); @@ -2329,7 +2344,7 @@ throw sqlEx; // don't re-wrap } catch (Exception e) { throw SQLError.createSQLException(Messages.getString( - "ResultSet.Bad_format_for_Date", new Object[] { new String(fromServer), + "ResultSet.Bad_format_for_Date", new Object[] { new String(dateAsBytes), new Integer(columnIndex) }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ } @@ -5903,7 +5918,7 @@ } else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(this.connection.getZeroDateTimeBehavior())) { throw SQLError.createSQLException("Value '" + timeAsString - + " can not be represented as java.sql.Time", + + "' can not be represented as java.sql.Time", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } @@ -6049,20 +6064,24 @@ int length = timeAsBytes.length; boolean allZeroTime = true; + boolean onlyTimePresent = true; for (int i = 0; i < length; i++) { - if (timeAsBytes[i] != '0' && - timeAsBytes[i] != ' ' && - timeAsBytes[i] != ':' && - timeAsBytes[i] != '-' && - timeAsBytes[i] != '/') { + byte b = timeAsBytes[i]; + + if (b == ' ' || b == '-' || b == '/') { + onlyTimePresent = false; + } + + if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' + && b != '.') { allZeroTime = false; - + break; } } - if (allZeroTime) { + if (!onlyTimePresent && allZeroTime) { if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(this.connection.getZeroDateTimeBehavior())) { this.wasNullFlag = true; @@ -6071,7 +6090,7 @@ } else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(this.connection.getZeroDateTimeBehavior())) { throw SQLError.createSQLException("Value '" + new String(timeAsBytes) - + " can not be represented as java.sql.Time", + + "' can not be represented as java.sql.Time", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } @@ -6345,7 +6364,7 @@ } else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(this.connection.getZeroDateTimeBehavior())) { throw SQLError.createSQLException("Value '" + timestampValue - + " can not be represented as java.sql.Timestamp", + + "' can not be represented as java.sql.Timestamp", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } @@ -6516,7 +6535,7 @@ .changeTimezone(this.connection, sessionCalendar, targetCalendar, - fastTimestampCreate(sessionCalendar, 70, 0, 1, + fastTimestampCreate(sessionCalendar, 1970, 1, 1, hour, minutes, seconds, 0), this.connection.getServerTimezoneTZ(), tz, rollForward); @@ -6606,20 +6625,20 @@ private Timestamp getTimestampFromBytes(int columnIndex, Calendar targetCalendar, - byte[] timestampValue, TimeZone tz, boolean rollForward) + byte[] timestampAsBytes, TimeZone tz, boolean rollForward) throws java.sql.SQLException { checkColumnBounds(columnIndex); try { this.wasNullFlag = false; - if (timestampValue == null) { + if (timestampAsBytes == null) { this.wasNullFlag = true; return null; } - int length = timestampValue.length; + int length = timestampAsBytes.length; Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ? this.connection.getUtcCalendar() : @@ -6628,19 +6647,24 @@ synchronized (sessionCalendar) { boolean allZeroTimestamp = true; + boolean onlyTimePresent = true; + for (int i = 0; i < length; i++) { - if (timestampValue[i] != '0' && - timestampValue[i] != ' ' && - timestampValue[i] != ':' && - timestampValue[i] != '-' && - timestampValue[i] != '/') { + byte b = timestampAsBytes[i]; + + if (b == ' ' || b == '-' || b == '/') { + onlyTimePresent = false; + } + + if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' + && b != '.') { allZeroTimestamp = false; - + break; } } - if (allZeroTimestamp) { + if (!onlyTimePresent && allZeroTimestamp) { if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(this.connection.getZeroDateTimeBehavior())) { @@ -6649,8 +6673,8 @@ return null; } else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(this.connection.getZeroDateTimeBehavior())) { - throw SQLError.createSQLException("Value '" + timestampValue - + " can not be represented as java.sql.Timestamp", + throw SQLError.createSQLException("Value '" + timestampAsBytes + + "' can not be represented as java.sql.Timestamp", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } @@ -6664,12 +6688,12 @@ sessionCalendar, targetCalendar, fastTimestampCreate(sessionCalendar, - StringUtils.getInt(timestampValue, 0, 4), 1, + StringUtils.getInt(timestampAsBytes, 0, 4), 1, 1, 0, 0, 0, 0), this.connection .getServerTimezoneTZ(), tz, rollForward); } else { - if (timestampValue[length - 1] == '.') { + if (timestampAsBytes[length - 1] == '.') { length--; } @@ -6683,21 +6707,21 @@ case 21: case 20: case 19: { - int year = StringUtils.getInt(timestampValue, 0, 4); - int month = StringUtils.getInt(timestampValue, 5, 7); - int day = StringUtils.getInt(timestampValue, 8, 10); - int hour = StringUtils.getInt(timestampValue, 11, 13); - int minutes = StringUtils.getInt(timestampValue, 14, 16); - int seconds = StringUtils.getInt(timestampValue, 17, 19); + int year = StringUtils.getInt(timestampAsBytes, 0, 4); + int month = StringUtils.getInt(timestampAsBytes, 5, 7); + int day = StringUtils.getInt(timestampAsBytes, 8, 10); + int hour = StringUtils.getInt(timestampAsBytes, 11, 13); + int minutes = StringUtils.getInt(timestampAsBytes, 14, 16); + int seconds = StringUtils.getInt(timestampAsBytes, 17, 19); int nanos = 0; if (length > 19) { - int decimalIndex = StringUtils.lastIndexOf(timestampValue, '.'); + int decimalIndex = StringUtils.lastIndexOf(timestampAsBytes, '.'); if (decimalIndex != -1) { if ((decimalIndex + 2) <= length) { - nanos = StringUtils.getInt(timestampValue, decimalIndex + 1, length); + nanos = StringUtils.getInt(timestampAsBytes, decimalIndex + 1, length); } else { throw new IllegalArgumentException(); // re-thrown // further @@ -6718,12 +6742,12 @@ } case 14: { - int year = StringUtils.getInt(timestampValue, 0, 4); - int month = StringUtils.getInt(timestampValue, 4, 6); - int day = StringUtils.getInt(timestampValue, 6, 8); - int hour = StringUtils.getInt(timestampValue, 8, 10); - int minutes = StringUtils.getInt(timestampValue, 10, 12); - int seconds = StringUtils.getInt(timestampValue, 12, 14); + int year = StringUtils.getInt(timestampAsBytes, 0, 4); + int month = StringUtils.getInt(timestampAsBytes, 4, 6); + int day = StringUtils.getInt(timestampAsBytes, 6, 8); + int hour = StringUtils.getInt(timestampAsBytes, 8, 10); + int minutes = StringUtils.getInt(timestampAsBytes, 10, 12); + int seconds = StringUtils.getInt(timestampAsBytes, 12, 14); return TimeUtil.changeTimezone(this.connection, sessionCalendar, @@ -6734,17 +6758,17 @@ } case 12: { - int year = StringUtils.getInt(timestampValue, 0, 2); + int year = StringUtils.getInt(timestampAsBytes, 0, 2); if (year <= 69) { year = (year + 100); } - int month = StringUtils.getInt(timestampValue, 2, 4); - int day = StringUtils.getInt(timestampValue, 4, 6); - int hour = StringUtils.getInt(timestampValue, 6, 8); - int minutes = StringUtils.getInt(timestampValue, 8, 10); - int seconds = StringUtils.getInt(timestampValue, 10, 12); + int month = StringUtils.getInt(timestampAsBytes, 2, 4); + int day = StringUtils.getInt(timestampAsBytes, 4, 6); + int hour = StringUtils.getInt(timestampAsBytes, 6, 8); + int minutes = StringUtils.getInt(timestampAsBytes, 8, 10); + int seconds = StringUtils.getInt(timestampAsBytes, 10, 12); return TimeUtil.changeTimezone(this.connection, sessionCalendar, @@ -6762,23 +6786,23 @@ int minutes; if ((this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) - || (StringUtils.indexOf(timestampValue, '-') != -1)) { - year = StringUtils.getInt(timestampValue, 0, 4); - month = StringUtils.getInt(timestampValue, 5, 7); - day = StringUtils.getInt(timestampValue, 8, 10); + || (StringUtils.indexOf(timestampAsBytes, '-') != -1)) { + year = StringUtils.getInt(timestampAsBytes, 0, 4); + month = StringUtils.getInt(timestampAsBytes, 5, 7); + day = StringUtils.getInt(timestampAsBytes, 8, 10); hour = 0; minutes = 0; } else { - year = StringUtils.getInt(timestampValue, 0, 2); + year = StringUtils.getInt(timestampAsBytes, 0, 2); if (year <= 69) { year = (year + 100); } - month = StringUtils.getInt(timestampValue, 2, 4); - day = StringUtils.getInt(timestampValue, 4, 6); - hour = StringUtils.getInt(timestampValue, 6, 8); - minutes = StringUtils.getInt(timestampValue, 8, 10); + month = StringUtils.getInt(timestampAsBytes, 2, 4); + day = StringUtils.getInt(timestampAsBytes, 4, 6); + hour = StringUtils.getInt(timestampAsBytes, 6, 8); + minutes = StringUtils.getInt(timestampAsBytes, 8, 10); year += 1900; // two-digit year } @@ -6792,25 +6816,25 @@ } case 8: { - if (StringUtils.indexOf(timestampValue, ':') != -1) { - int hour = StringUtils.getInt(timestampValue, 0, 2); - int minutes = StringUtils.getInt(timestampValue, 3, 5); - int seconds = StringUtils.getInt(timestampValue, 6, 8); + if (StringUtils.indexOf(timestampAsBytes, ':') != -1) { + int hour = StringUtils.getInt(timestampAsBytes, 0, 2); + int minutes = StringUtils.getInt(timestampAsBytes, 3, 5); + int seconds = StringUtils.getInt(timestampAsBytes, 6, 8); return TimeUtil .changeTimezone(this.connection, sessionCalendar, targetCalendar, - fastTimestampCreate(sessionCalendar, 70, 0, 1, + fastTimestampCreate(sessionCalendar, 1970, 1, 1, hour, minutes, seconds, 0), this.connection.getServerTimezoneTZ(), tz, rollForward); } - int year = StringUtils.getInt(timestampValue, 0, 4); - int month = StringUtils.getInt(timestampValue, 4, 6); - int day = StringUtils.getInt(timestampValue, 6, 8); + int year = StringUtils.getInt(timestampAsBytes, 0, 4); + int month = StringUtils.getInt(timestampAsBytes, 4, 6); + int day = StringUtils.getInt(timestampAsBytes, 6, 8); return TimeUtil.changeTimezone(this.connection, sessionCalendar, @@ -6821,14 +6845,14 @@ } case 6: { - int year = StringUtils.getInt(timestampValue, 0, 2); + int year = StringUtils.getInt(timestampAsBytes, 0, 2); if (year <= 69) { year = (year + 100); } - int month = StringUtils.getInt(timestampValue, 2, 4); - int day = StringUtils.getInt(timestampValue, 4, 6); + int month = StringUtils.getInt(timestampAsBytes, 2, 4); + int day = StringUtils.getInt(timestampAsBytes, 4, 6); return TimeUtil.changeTimezone(this.connection, sessionCalendar, @@ -6839,13 +6863,13 @@ } case 4: { - int year = StringUtils.getInt(timestampValue, 0, 2); + int year = StringUtils.getInt(timestampAsBytes, 0, 2); if (year <= 69) { year = (year + 100); } - int month = StringUtils.getInt(timestampValue, 2, 4); + int month = StringUtils.getInt(timestampAsBytes, 2, 4); return TimeUtil.changeTimezone(this.connection, sessionCalendar, @@ -6856,7 +6880,7 @@ } case 2: { - int year = StringUtils.getInt(timestampValue, 0, 2); + int year = StringUtils.getInt(timestampAsBytes, 0, 2); if (year <= 69) { year = (year + 100); @@ -6872,7 +6896,7 @@ default: throw new java.sql.SQLException( - "Bad format for Timestamp '" + new String(timestampValue) + "Bad format for Timestamp '" + new String(timestampAsBytes) + "' in column " + columnIndex + ".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } @@ -6880,7 +6904,7 @@ } } catch (Exception e) { throw new java.sql.SQLException("Cannot convert value '" - + new String(timestampValue) + "' from column " + columnIndex + + new String(timestampAsBytes) + "' from column " + columnIndex + " to TIMESTAMP.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } Modified: branches/branch_5_0/connector-j/src/testsuite/regression/ResultSetRegressionTest.java =================================================================== --- branches/branch_5_0/connector-j/src/testsuite/regression/ResultSetRegressionTest.java 2007-03-16 21:37:55 UTC (rev 6357) +++ branches/branch_5_0/connector-j/src/testsuite/regression/ResultSetRegressionTest.java 2007-03-21 20:01:27 UTC (rev 6358) @@ -3965,7 +3965,7 @@ closeMemberJDBCResources(); } } - + /** * Tests fix for BUG#26173 - fetching rows via cursor retrieves * corrupted data. @@ -4011,4 +4011,51 @@ closeMemberJDBCResources(); } } + + /** + * Tests fix for BUG#26789 - fast date/time parsing doesn't take into + * account 00:00:00 as a legal value. + * + * @throws Exception + * if the test fails + */ + public void testBug26789() throws Exception { + try { + this.rs = this.stmt.executeQuery("SELECT '00:00:00'"); + this.rs.next(); + this.rs.getTime(1); + assertEquals("00:00:00", this.rs.getTime(1).toString()); + assertEquals("1970-01-01 00:00:00.0", this.rs.getTimestamp(1) + .toString()); + assertEquals("1970-01-01", this.rs.getDate(1).toString()); + + this.rs.close(); + + this.rs = this.stmt.executeQuery("SELECT '00/00/0000 00:00:00'"); + this.rs.next(); + + try { + this.rs.getTime(1); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx + .getSQLState()); + } + + try { + this.rs.getTimestamp(1); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx + .getSQLState()); + } + + try { + this.rs.getDate(1); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx + .getSQLState()); + } + } finally { + closeMemberJDBCResources(); + } + } } Modified: trunk/connector-j/CHANGES =================================================================== --- trunk/connector-j/CHANGES 2007-03-16 21:37:55 UTC (rev 6357) +++ trunk/connector-j/CHANGES 2007-03-21 20:01:27 UTC (rev 6358) @@ -54,7 +54,10 @@ - Give better error message when "streaming" result sets, and the connection gets clobbered because of exceeding net_write_timeout on the server. (which is basically what the error message says too). - + + - Fixed BUG#26789 - fast date/time parsing doesn't take into + account 00:00:00 as a legal value. + 03-01-07 - Version 5.0.5 - Fixed BUG#23645 - Some collations/character sets reported as "unknown" Modified: trunk/connector-j/src/com/mysql/jdbc/ResultSet.java =================================================================== --- trunk/connector-j/src/com/mysql/jdbc/ResultSet.java 2007-03-16 21:37:55 UTC (rev 6357) +++ trunk/connector-j/src/com/mysql/jdbc/ResultSet.java 2007-03-21 20:01:27 UTC (rev 6358) @@ -2221,6 +2221,10 @@ return fastDateCreate(null, 1970, 1, 1); // Return EPOCH } else { if (stringVal.length() < 10) { + if (stringVal.length() == 8) { + return fastDateCreate(null, 1970, 1, 1); // Return EPOCH for TIME + } + throw SQLError.createSQLException(Messages.getString( "ResultSet.Bad_format_for_Date", new Object[] { stringVal, new Integer(columnIndex) }), @@ -2252,7 +2256,7 @@ } } - private final java.sql.Date getDateFromBytes(byte[] fromServer, + private final java.sql.Date getDateFromBytes(byte[] dateAsBytes, int columnIndex) throws SQLException { checkColumnBounds(columnIndex); @@ -2263,27 +2267,35 @@ try { this.wasNullFlag = false; - if (fromServer == null) { + if (dateAsBytes == null) { this.wasNullFlag = true; return null; } + boolean allZeroDate = true; - for (int i = 0; i < fromServer.length; i++) { - if (fromServer[i] != '0' && - fromServer[i] != ' ' && - fromServer[i] != ':' && - fromServer[i] != '-' && - fromServer[i] != '/') { + boolean onlyTimePresent = true; + + int length = dateAsBytes.length; + + for (int i = 0; i < length; i++) { + byte b = dateAsBytes[i]; + + if (b == ' ' || b == '-' || b == '/') { + onlyTimePresent = false; + } + + if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' + && b != '.') { allZeroDate = false; - + break; } } - if (allZeroDate) { + if (!onlyTimePresent && allZeroDate) { if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(this.connection.getZeroDateTimeBehavior())) { @@ -2292,7 +2304,7 @@ return null; } else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(this.connection.getZeroDateTimeBehavior())) { - throw SQLError.createSQLException("Value '" + new String(fromServer) + throw SQLError.createSQLException("Value '" + new String(dateAsBytes) + "' can not be represented as java.sql.Date", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } @@ -2303,21 +2315,21 @@ } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { // Convert from TIMESTAMP - switch (fromServer.length) { + switch (length) { case 21: case 19: { // java.sql.Timestamp format - year = StringUtils.getInt(fromServer, 0, 4); - month = StringUtils.getInt(fromServer, 5, 7); - day = StringUtils.getInt(fromServer, 8, 10); + year = StringUtils.getInt(dateAsBytes, 0, 4); + month = StringUtils.getInt(dateAsBytes, 5, 7); + day = StringUtils.getInt(dateAsBytes, 8, 10); return fastDateCreate(null, year, month, day); } case 14: case 8: { - year = StringUtils.getInt(fromServer, 0, 4); - month = StringUtils.getInt(fromServer, 4, 6); - day = StringUtils.getInt(fromServer, 6, 8); + year = StringUtils.getInt(dateAsBytes, 0, 4); + month = StringUtils.getInt(dateAsBytes, 4, 6); + day = StringUtils.getInt(dateAsBytes, 6, 8); return fastDateCreate(null, year, month, day); } @@ -2325,32 +2337,32 @@ case 12: case 10: case 6: { - year = StringUtils.getInt(fromServer, 0, 2); + year = StringUtils.getInt(dateAsBytes, 0, 2); if (year <= 69) { year = year + 100; } - month = StringUtils.getInt(fromServer, 2, 4); - day = StringUtils.getInt(fromServer, 4, 6); + month = StringUtils.getInt(dateAsBytes, 2, 4); + day = StringUtils.getInt(dateAsBytes, 4, 6); return fastDateCreate(null, year + 1900, month, day); } case 4: { - year = StringUtils.getInt(fromServer, 0, 4); + year = StringUtils.getInt(dateAsBytes, 0, 4); if (year <= 69) { year = year + 100; } - month = StringUtils.getInt(fromServer, 2, 4); + month = StringUtils.getInt(dateAsBytes, 2, 4); return fastDateCreate(null, year + 1900, month, 1); } case 2: { - year = StringUtils.getInt(fromServer, 0, 2); + year = StringUtils.getInt(dateAsBytes, 0, 2); if (year <= 69) { year = year + 100; @@ -2362,13 +2374,13 @@ default: throw SQLError.createSQLException(Messages.getString( "ResultSet.Bad_format_for_Date", new Object[] { - new String(fromServer), new Integer(columnIndex) }), + new String(dateAsBytes), new Integer(columnIndex) }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ } /* endswitch */ } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { - if (fromServer.length == 2 || fromServer.length == 1) { - year = StringUtils.getInt(fromServer); + if (length == 2 || length == 1) { + year = StringUtils.getInt(dateAsBytes); if (year <= 69) { year = year + 100; @@ -2376,27 +2388,31 @@ year += 1900; } else { - year = StringUtils.getInt(fromServer, 0, 4); + year = StringUtils.getInt(dateAsBytes, 0, 4); } return fastDateCreate(null, year, 1, 1); } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIME) { return fastDateCreate(null, 1970, 1, 1); // Return EPOCH } else { - if (fromServer.length < 10) { + if (length < 10) { + if (length == 8) { + return fastDateCreate(null, 1970, 1, 1); // Return EPOCH for TIME + } + throw SQLError.createSQLException(Messages.getString( "ResultSet.Bad_format_for_Date", new Object[] { - new String(fromServer), new Integer(columnIndex) }), + new String(dateAsBytes), new Integer(columnIndex) }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ } - if (fromServer.length != 18) { - year = StringUtils.getInt(fromServer, 0, 4); - month = StringUtils.getInt(fromServer, 5, 7); - day = StringUtils.getInt(fromServer, 8, 10); + if (length != 18) { + year = StringUtils.getInt(dateAsBytes, 0, 4); + month = StringUtils.getInt(dateAsBytes, 5, 7); + day = StringUtils.getInt(dateAsBytes, 8, 10); } else { // JDK-1.3 timestamp format, not real easy to parse positionally :p - StringTokenizer st = new StringTokenizer(new String(fromServer), "- "); + StringTokenizer st = new StringTokenizer(new String(dateAsBytes), "- "); year = Integer.parseInt(st.nextToken()); month = Integer.parseInt(st.nextToken()); @@ -2409,7 +2425,7 @@ throw sqlEx; // don't re-wrap } catch (Exception e) { throw SQLError.createSQLException(Messages.getString( - "ResultSet.Bad_format_for_Date", new Object[] { new String(fromServer), + "ResultSet.Bad_format_for_Date", new Object[] { new String(dateAsBytes), new Integer(columnIndex) }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ } @@ -6002,7 +6018,7 @@ } else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(this.connection.getZeroDateTimeBehavior())) { throw SQLError.createSQLException("Value '" + timeAsString - + " can not be represented as java.sql.Time", + + "' can not be represented as java.sql.Time", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } @@ -6021,16 +6037,16 @@ switch (length) { case 19: { // YYYY-MM-DD hh:mm:ss - - hr = Integer.parseInt(timeAsString.substring(length - 8, - length - 6)); - min = Integer.parseInt(timeAsString.substring(length - 5, - length - 3)); - sec = Integer.parseInt(timeAsString.substring(length - 2, - length)); + + hr = Integer.parseInt(timeAsString.substring(length - 8, + length - 6)); + min = Integer.parseInt(timeAsString.substring(length - 5, + length - 3)); + sec = Integer.parseInt(timeAsString.substring(length - 2, + length)); } - - break; + + break; case 14: case 12: { hr = Integer.parseInt(timeAsString.substring(length - 6, @@ -6148,20 +6164,24 @@ int length = timeAsBytes.length; boolean allZeroTime = true; + boolean onlyTimePresent = true; for (int i = 0; i < length; i++) { - if (timeAsBytes[i] != '0' && - timeAsBytes[i] != ' ' && - timeAsBytes[i] != ':' && - timeAsBytes[i] != '-' && - timeAsBytes[i] != '/') { + byte b = timeAsBytes[i]; + + if (b == ' ' || b == '-' || b == '/') { + onlyTimePresent = false; + } + + if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' + && b != '.') { allZeroTime = false; - + break; } } - if (allZeroTime) { + if (!onlyTimePresent && allZeroTime) { if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(this.connection.getZeroDateTimeBehavior())) { this.wasNullFlag = true; @@ -6170,7 +6190,7 @@ } else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(this.connection.getZeroDateTimeBehavior())) { throw SQLError.createSQLException("Value '" + new String(timeAsBytes) - + " can not be represented as java.sql.Time", + + "' can not be represented as java.sql.Time", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } @@ -6444,7 +6464,7 @@ } else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(this.connection.getZeroDateTimeBehavior())) { throw SQLError.createSQLException("Value '" + timestampValue - + " can not be represented as java.sql.Timestamp", + + "' can not be represented as java.sql.Timestamp", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } @@ -6615,7 +6635,7 @@ .changeTimezone(this.connection, sessionCalendar, targetCalendar, - fastTimestampCreate(sessionCalendar, 70, 0, 1, + fastTimestampCreate(sessionCalendar, 1970, 1, 1, hour, minutes, seconds, 0), this.connection.getServerTimezoneTZ(), tz, rollForward); @@ -6705,23 +6725,21 @@ private Timestamp getTimestampFromBytes(int columnIndex, Calendar targetCalendar, - byte[] timestampValue, TimeZone tz, boolean rollForward) + byte[] timestampAsBytes, TimeZone tz, boolean rollForward) throws java.sql.SQLException { checkColumnBounds(columnIndex); try { this.wasNullFlag = false; - if (timestampValue == null) { + if (timestampAsBytes == null) { this.wasNullFlag = true; return null; } + + int length = timestampAsBytes.length; - - - int length = timestampValue.length; - Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ? this.connection.getUtcCalendar() : getCalendarInstanceForSessionOrNew(); @@ -6729,19 +6747,24 @@ synchronized (sessionCalendar) { boolean allZeroTimestamp = true; + boolean onlyTimePresent = true; + for (int i = 0; i < length; i++) { - if (timestampValue[i] != '0' && - timestampValue[i] != ' ' && - timestampValue[i] != ':' && - timestampValue[i] != '-' && - timestampValue[i] != '/') { + byte b = timestampAsBytes[i]; + + if (b == ' ' || b == '-' || b == '/') { + onlyTimePresent = false; + } + + if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' + && b != '.') { allZeroTimestamp = false; - + break; } } - if (allZeroTimestamp) { + if (!onlyTimePresent && allZeroTimestamp) { if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(this.connection.getZeroDateTimeBehavior())) { @@ -6750,8 +6773,8 @@ return null; } else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(this.connection.getZeroDateTimeBehavior())) { - throw SQLError.createSQLException("Value '" + timestampValue - + " can not be represented as java.sql.Timestamp", + throw SQLError.createSQLException("Value '" + timestampAsBytes + + "' can not be represented as java.sql.Timestamp", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } @@ -6765,12 +6788,12 @@ sessionCalendar, targetCalendar, fastTimestampCreate(sessionCalendar, - StringUtils.getInt(timestampValue, 0, 4), 1, + StringUtils.getInt(timestampAsBytes, 0, 4), 1, 1, 0, 0, 0, 0), this.connection .getServerTimezoneTZ(), tz, rollForward); } else { - if (timestampValue[length - 1] == '.') { + if (timestampAsBytes[length - 1] == '.') { length--; } @@ -6784,21 +6807,21 @@ case 21: case 20: case 19: { - int year = StringUtils.getInt(timestampValue, 0, 4); - int month = StringUtils.getInt(timestampValue, 5, 7); - int day = StringUtils.getInt(timestampValue, 8, 10); - int hour = StringUtils.getInt(timestampValue, 11, 13); - int minutes = StringUtils.getInt(timestampValue, 14, 16); - int seconds = StringUtils.getInt(timestampValue, 17, 19); + int year = StringUtils.getInt(timestampAsBytes, 0, 4); + int month = StringUtils.getInt(timestampAsBytes, 5, 7); + int day = StringUtils.getInt(timestampAsBytes, 8, 10); + int hour = StringUtils.getInt(timestampAsBytes, 11, 13); + int minutes = StringUtils.getInt(timestampAsBytes, 14, 16); + int seconds = StringUtils.getInt(timestampAsBytes, 17, 19); int nanos = 0; if (length > 19) { - int decimalIndex = StringUtils.lastIndexOf(timestampValue, '.'); + int decimalIndex = StringUtils.lastIndexOf(timestampAsBytes, '.'); if (decimalIndex != -1) { if ((decimalIndex + 2) <= length) { - nanos = StringUtils.getInt(timestampValue, decimalIndex + 1, length); + nanos = StringUtils.getInt(timestampAsBytes, decimalIndex + 1, length); } else { throw new IllegalArgumentException(); // re-thrown // further @@ -6819,12 +6842,12 @@ } case 14: { - int year = StringUtils.getInt(timestampValue, 0, 4); - int month = StringUtils.getInt(timestampValue, 4, 6); - int day = StringUtils.getInt(timestampValue, 6, 8); - int hour = StringUtils.getInt(timestampValue, 8, 10); - int minutes = StringUtils.getInt(timestampValue, 10, 12); - int seconds = StringUtils.getInt(timestampValue, 12, 14); + int year = StringUtils.getInt(timestampAsBytes, 0, 4); + int month = StringUtils.getInt(timestampAsBytes, 4, 6); + int day = StringUtils.getInt(timestampAsBytes, 6, 8); + int hour = StringUtils.getInt(timestampAsBytes, 8, 10); + int minutes = StringUtils.getInt(timestampAsBytes, 10, 12); + int seconds = StringUtils.getInt(timestampAsBytes, 12, 14); return TimeUtil.changeTimezone(this.connection, sessionCalendar, @@ -6835,17 +6858,17 @@ } case 12: { - int year = StringUtils.getInt(timestampValue, 0, 2); + int year = StringUtils.getInt(timestampAsBytes, 0, 2); if (year <= 69) { year = (year + 100); } - int month = StringUtils.getInt(timestampValue, 2, 4); - int day = StringUtils.getInt(timestampValue, 4, 6); - int hour = StringUtils.getInt(timestampValue, 6, 8); - int minutes = StringUtils.getInt(timestampValue, 8, 10); - int seconds = StringUtils.getInt(timestampValue, 10, 12); + int month = StringUtils.getInt(timestampAsBytes, 2, 4); + int day = StringUtils.getInt(timestampAsBytes, 4, 6); + int hour = StringUtils.getInt(timestampAsBytes, 6, 8); + int minutes = StringUtils.getInt(timestampAsBytes, 8, 10); + int seconds = StringUtils.getInt(timestampAsBytes, 10, 12); return TimeUtil.changeTimezone(this.connection, sessionCalendar, @@ -6863,23 +6886,23 @@ int minutes; if ((this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) - || (StringUtils.indexOf(timestampValue, '-') != -1)) { - year = StringUtils.getInt(timestampValue, 0, 4); - month = StringUtils.getInt(timestampValue, 5, 7); - day = StringUtils.getInt(timestampValue, 8, 10); + || (StringUtils.indexOf(timestampAsBytes, '-') != -1)) { + year = StringUtils.getInt(timestampAsBytes, 0, 4); + month = StringUtils.getInt(timestampAsBytes, 5, 7); + day = StringUtils.getInt(timestampAsBytes, 8, 10); hour = 0; minutes = 0; } else { - year = StringUtils.getInt(timestampValue, 0, 2); + year = StringUtils.getInt(timestampAsBytes, 0, 2); if (year <= 69) { year = (year + 100); } - month = StringUtils.getInt(timestampValue, 2, 4); - day = StringUtils.getInt(timestampValue, 4, 6); - hour = StringUtils.getInt(timestampValue, 6, 8); - minutes = StringUtils.getInt(timestampValue, 8, 10); + month = StringUtils.getInt(timestampAsBytes, 2, 4); + day = StringUtils.getInt(timestampAsBytes, 4, 6); + hour = StringUtils.getInt(timestampAsBytes, 6, 8); + minutes = StringUtils.getInt(timestampAsBytes, 8, 10); year += 1900; // two-digit year } @@ -6893,25 +6916,25 @@ } case 8: { - if (StringUtils.indexOf(timestampValue, ':') != -1) { - int hour = StringUtils.getInt(timestampValue, 0, 2); - int minutes = StringUtils.getInt(timestampValue, 3, 5); - int seconds = StringUtils.getInt(timestampValue, 6, 8); + if (StringUtils.indexOf(timestampAsBytes, ':') != -1) { + int hour = StringUtils.getInt(timestampAsBytes, 0, 2); + int minutes = StringUtils.getInt(timestampAsBytes, 3, 5); + int seconds = StringUtils.getInt(timestampAsBytes, 6, 8); return TimeUtil .changeTimezone(this.connection, sessionCalendar, targetCalendar, - fastTimestampCreate(sessionCalendar, 70, 0, 1, + fastTimestampCreate(sessionCalendar, 1970, 1, 1, hour, minutes, seconds, 0), this.connection.getServerTimezoneTZ(), tz, rollForward); } - int year = StringUtils.getInt(timestampValue, 0, 4); - int month = StringUtils.getInt(timestampValue, 4, 6); - int day = StringUtils.getInt(timestampValue, 6, 8); + int year = StringUtils.getInt(timestampAsBytes, 0, 4); + int month = StringUtils.getInt(timestampAsBytes, 4, 6); + int day = StringUtils.getInt(timestampAsBytes, 6, 8); return TimeUtil.changeTimezone(this.connection, sessionCalendar, @@ -6922,14 +6945,14 @@ } case 6: { - int year = StringUtils.getInt(timestampValue, 0, 2); + int year = StringUtils.getInt(timestampAsBytes, 0, 2); if (year <= 69) { year = (year + 100); } - int month = StringUtils.getInt(timestampValue, 2, 4); - int day = StringUtils.getInt(timestampValue, 4, 6); + int month = StringUtils.getInt(timestampAsBytes, 2, 4); + int day = StringUtils.getInt(timestampAsBytes, 4, 6); return TimeUtil.changeTimezone(this.connection, sessionCalendar, @@ -6940,13 +6963,13 @@ } case 4: { - int year = StringUtils.getInt(timestampValue, 0, 2); + int year = StringUtils.getInt(timestampAsBytes, 0, 2); if (year <= 69) { year = (year + 100); } - int month = StringUtils.getInt(timestampValue, 2, 4); + int month = StringUtils.getInt(timestampAsBytes, 2, 4); return TimeUtil.changeTimezone(this.connection, sessionCalendar, @@ -6957,7 +6980,7 @@ } case 2: { - int year = StringUtils.getInt(timestampValue, 0, 2); + int year = StringUtils.getInt(timestampAsBytes, 0, 2); if (year <= 69) { year = (year + 100); @@ -6973,7 +6996,7 @@ default: throw new java.sql.SQLException( - "Bad format for Timestamp '" + new String(timestampValue) + "Bad format for Timestamp '" + new String(timestampAsBytes) + "' in column " + columnIndex + ".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } @@ -6981,7 +7004,7 @@ } } catch (Exception e) { throw new java.sql.SQLException("Cannot convert value '" - + new String(timestampValue) + "' from column " + columnIndex + + new String(timestampAsBytes) + "' from column " + columnIndex + " to TIMESTAMP.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } Modified: trunk/connector-j/src/testsuite/regression/ResultSetRegressionTest.java =================================================================== --- trunk/connector-j/src/testsuite/regression/ResultSetRegressionTest.java 2007-03-16 21:37:55 UTC (rev 6357) +++ trunk/connector-j/src/testsuite/regression/ResultSetRegressionTest.java 2007-03-21 20:01:27 UTC (rev 6358) @@ -4011,4 +4011,51 @@ closeMemberJDBCResources(); } } + + /** + * Tests fix for BUG#26789 - fast date/time parsing doesn't take into + * account 00:00:00 as a legal value. + * + * @throws Exception + * if the test fails + */ + public void testBug26789() throws Exception { + try { + this.rs = this.stmt.executeQuery("SELECT '00:00:00'"); + this.rs.next(); + this.rs.getTime(1); + assertEquals("00:00:00", this.rs.getTime(1).toString()); + assertEquals("1970-01-01 00:00:00.0", this.rs.getTimestamp(1) + .toString()); + assertEquals("1970-01-01", this.rs.getDate(1).toString()); + + this.rs.close(); + + this.rs = this.stmt.executeQuery("SELECT '00/00/0000 00:00:00'"); + this.rs.next(); + + try { + this.rs.getTime(1); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx + .getSQLState()); + } + + try { + this.rs.getTimestamp(1); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx + .getSQLState()); + } + + try { + this.rs.getDate(1); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx + .getSQLState()); + } + } finally { + closeMemberJDBCResources(); + } + } }