/* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/J is licensed under the terms of the GPLv2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception <http://www.mysql.com/about/legal/licensing/foss-exception.html>. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package com.mysql.jdbc; import java.io.InputStream; import java.io.Reader; import java.sql.Date; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.Calendar; import java.util.StringTokenizer; import java.util.TimeZone; /** * Classes that implement this interface represent one row of data from the * MySQL server that might be stored in different ways depending on whether the * result set was streaming (so they wrap a reusable packet), or whether the * result set was cached or via a server-side cursor (so they represent a * byte[][]). * * Notice that <strong>no</strong> bounds checking is expected for implementors * of this interface, it happens in ResultSetImpl. * * @version $Id: $ */ public abstract class ResultSetRow { protected ExceptionInterceptor exceptionInterceptor; protected ResultSetRow(ExceptionInterceptor exceptionInterceptor) { this.exceptionInterceptor = exceptionInterceptor; } /** * The metadata of the fields of this result set. */ protected Field[] metadata; /** * Called during navigation to next row to close all open * streams. */ public abstract void closeOpenStreams(); /** * Returns data at the given index as an InputStream with no * character conversion. * * @param columnIndex * of the column value (starting at 0) to return. * @return the value at the given index as an InputStream or null * if null. * * @throws SQLException if an error occurs while retrieving the value. */ public abstract InputStream getBinaryInputStream(int columnIndex) throws SQLException; /** * Returns the value at the given column (index starts at 0) "raw" (i.e. * as-returned by the server). * * @param index * of the column value (starting at 0) to return. * @return the value for the given column (including NULL if it is) * @throws SQLException * if an error occurs while retrieving the value. */ public abstract byte[] getColumnValue(int index) throws SQLException; protected final java.sql.Date getDateFast(int columnIndex, byte[] dateAsBytes, int offset, int length, MySQLConnection conn, ResultSetImpl rs, Calendar targetCalendar) throws SQLException { int year = 0; int month = 0; int day = 0; try { if (dateAsBytes == null) { return null; } boolean allZeroDate = true; boolean onlyTimePresent = false; for (int i = 0; i < length; i++) { if (dateAsBytes[offset + i] == ':') { onlyTimePresent = true; break; } } for (int i = 0; i < length; i++) { byte b = dateAsBytes[offset + i]; if (b == ' ' || b == '-' || b == '/') { onlyTimePresent = false; } if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' && b != '.') { allZeroDate = false; break; } } if (!onlyTimePresent && allZeroDate) { if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(conn.getZeroDateTimeBehavior())) { return null; } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(conn.getZeroDateTimeBehavior())) { throw SQLError.createSQLException("Value '" + StringUtils.toString(dateAsBytes) + "' can not be represented as java.sql.Date", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); } // We're left with the case of 'round' to a date Java _can_ // represent, which is '0001-01-01'. return rs.fastDateCreate(targetCalendar, 1, 1, 1); } else if (this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { // Convert from TIMESTAMP switch (length) { case 29: case 21: case 19: { // java.sql.Timestamp format year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 4); month = StringUtils.getInt(dateAsBytes, offset + 5, offset + 7); day = StringUtils.getInt(dateAsBytes, offset + 8, offset + 10); return rs.fastDateCreate(targetCalendar, year, month, day); } case 14: case 8: { year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 4); month = StringUtils.getInt(dateAsBytes, offset + 4, offset + 6); day = StringUtils.getInt(dateAsBytes, offset + 6, offset + 8); return rs.fastDateCreate(targetCalendar, year, month, day); } case 12: case 10: case 6: { year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 2); if (year <= 69) { year = year + 100; } month = StringUtils.getInt(dateAsBytes, offset + 2, offset + 4); day = StringUtils.getInt(dateAsBytes, offset + 4, offset + 6); return rs.fastDateCreate(targetCalendar, year + 1900, month, day); } case 4: { year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 4); if (year <= 69) { year = year + 100; } month = StringUtils.getInt(dateAsBytes, offset + 2, offset + 4); return rs.fastDateCreate(targetCalendar, year + 1900, month, 1); } case 2: { year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 2); if (year <= 69) { year = year + 100; } return rs.fastDateCreate(targetCalendar, year + 1900, 1, 1); } default: throw SQLError .createSQLException( Messages .getString( "ResultSet.Bad_format_for_Date", new Object[] { StringUtils.toString( dateAsBytes), Integer.valueOf(columnIndex + 1) }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); //$NON-NLS-1$ } /* endswitch */ } else if (this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { if (length == 2 || length == 1) { year = StringUtils.getInt(dateAsBytes, offset, offset + length); if (year <= 69) { year = year + 100; } year += 1900; } else { year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 4); } return rs.fastDateCreate(targetCalendar, year, 1, 1); } else if (this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_TIME) { return rs.fastDateCreate(targetCalendar, 1970, 1, 1); // Return EPOCH } else { if (length < 10) { if (length == 8) { return rs.fastDateCreate(targetCalendar, 1970, 1, 1); // Return // EPOCH for // TIME } throw SQLError .createSQLException( Messages .getString( "ResultSet.Bad_format_for_Date", new Object[] { StringUtils.toString( dateAsBytes), Integer.valueOf(columnIndex + 1) }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); //$NON-NLS-1$ } if (length != 18) { year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 4); month = StringUtils.getInt(dateAsBytes, offset + 5, offset + 7); day = StringUtils.getInt(dateAsBytes, offset + 8, offset + 10); } else { // JDK-1.3 timestamp format, not real easy to parse // positionally :p StringTokenizer st = new StringTokenizer(StringUtils.toString( dateAsBytes, offset, length, "ISO8859_1"), "- "); year = Integer.parseInt(st.nextToken()); month = Integer.parseInt(st.nextToken()); day = Integer.parseInt(st.nextToken()); } } return rs.fastDateCreate(targetCalendar, year, month, day); } catch (SQLException sqlEx) { throw sqlEx; // don't re-wrap } catch (Exception e) { SQLException sqlEx = SQLError.createSQLException(Messages.getString( "ResultSet.Bad_format_for_Date", new Object[] { StringUtils.toString(dateAsBytes), Integer.valueOf(columnIndex + 1) }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); //$NON-NLS-1$ sqlEx.initCause(e); throw sqlEx; } } public abstract java.sql.Date getDateFast(int columnIndex, MySQLConnection conn, ResultSetImpl rs, Calendar targetCalendar) throws SQLException; /** * Returns the value at the given column (index starts at 0) as an int. * * * @param index * of the column value (starting at 0) to return. * @return the value for the given column (returns 0 if NULL, use isNull() * to determine if the value was actually NULL) * @throws SQLException * if an error occurs while retrieving the value. */ public abstract int getInt(int columnIndex) throws SQLException; /** * Returns the value at the given column (index starts at 0) as a long. * * * @param index * of the column value (starting at 0) to return. * @return the value for the given column (returns 0 if NULL, use isNull() * to determine if the value was actually NULL) * @throws SQLException * if an error occurs while retrieving the value. */ public abstract long getLong(int columnIndex) throws SQLException; /** * * @param columnIndex * @param bits * @param offset * @param length * @param conn * @param rs * @param cal * @return * @throws SQLException */ protected java.sql.Date getNativeDate(int columnIndex, byte[] bits, int offset, int length, MySQLConnection conn, ResultSetImpl rs, Calendar cal) throws SQLException { int year = 0; int month = 0; int day = 0; if (length != 0) { year = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8); month = bits[offset + 2]; day = bits[offset + 3]; } if (length == 0 || ((year == 0) && (month == 0) && (day == 0))) { if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(conn.getZeroDateTimeBehavior())) { return null; } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(conn.getZeroDateTimeBehavior())) { throw SQLError .createSQLException( "Value '0000-00-00' can not be represented as java.sql.Date", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); } year = 1; month = 1; day = 1; } if (!rs.useLegacyDatetimeCode) { return TimeUtil.fastDateCreate(year, month, day, cal); } return rs.fastDateCreate(cal == null ? rs.getCalendarInstanceForSessionOrNew() : cal, year, month, day); } public abstract Date getNativeDate(int columnIndex, MySQLConnection conn, ResultSetImpl rs, Calendar cal) throws SQLException; protected Object getNativeDateTimeValue(int columnIndex, byte[] bits, int offset, int length, Calendar targetCalendar, int jdbcType, int mysqlType, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) throws SQLException { int year = 0; int month = 0; int day = 0; int hour = 0; int minute = 0; int seconds = 0; int nanos = 0; if (bits == null) { return null; } Calendar sessionCalendar = conn.getUseJDBCCompliantTimezoneShift() ? conn .getUtcCalendar() : rs.getCalendarInstanceForSessionOrNew(); boolean populatedFromDateTimeValue = false; switch (mysqlType) { case MysqlDefs.FIELD_TYPE_DATETIME: case MysqlDefs.FIELD_TYPE_TIMESTAMP: populatedFromDateTimeValue = true; if (length != 0) { year = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8); month = bits[offset + 2]; day = bits[offset + 3]; if (length > 4) { hour = bits[offset + 4]; minute = bits[offset + 5]; seconds = bits[offset + 6]; } if (length > 7) { // MySQL uses microseconds nanos = ((bits[offset + 7] & 0xff) | ((bits[offset + 8] & 0xff) << 8) | ((bits[offset + 9] & 0xff) << 16) | ((bits[offset + 10] & 0xff) << 24)) * 1000; } } break; case MysqlDefs.FIELD_TYPE_DATE: populatedFromDateTimeValue = true; if (bits.length != 0) { year = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8); month = bits[offset + 2]; day = bits[offset + 3]; } break; case MysqlDefs.FIELD_TYPE_TIME: populatedFromDateTimeValue = true; if (bits.length != 0) { // bits[0] // skip tm->neg // binaryData.readLong(); // skip daysPart hour = bits[offset + 5]; minute = bits[offset + 6]; seconds = bits[offset + 7]; } year = 1970; month = 1; day = 1; break; default: populatedFromDateTimeValue = false; } switch (jdbcType) { case Types.TIME: if (populatedFromDateTimeValue) { if (!rs.useLegacyDatetimeCode) { return TimeUtil.fastTimeCreate(hour, minute, seconds, targetCalendar, this.exceptionInterceptor); } Time time = TimeUtil.fastTimeCreate(rs .getCalendarInstanceForSessionOrNew(), hour, minute, seconds, this.exceptionInterceptor); Time adjustedTime = TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, time, conn .getServerTimezoneTZ(), tz, rollForward); return adjustedTime; } return rs.getNativeTimeViaParseConversion(columnIndex + 1, targetCalendar, tz, rollForward); case Types.DATE: if (populatedFromDateTimeValue) { if ((year == 0) && (month == 0) && (day == 0)) { if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(conn.getZeroDateTimeBehavior())) { return null; } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(conn.getZeroDateTimeBehavior())) { throw new SQLException( "Value '0000-00-00' can not be represented as java.sql.Date", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } year = 1; month = 1; day = 1; } if (!rs.useLegacyDatetimeCode) { return TimeUtil.fastDateCreate(year, month, day, targetCalendar); } return rs .fastDateCreate( rs.getCalendarInstanceForSessionOrNew(), year, month, day); } return rs.getNativeDateViaParseConversion(columnIndex + 1); case Types.TIMESTAMP: if (populatedFromDateTimeValue) { if ((year == 0) && (month == 0) && (day == 0)) { if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(conn.getZeroDateTimeBehavior())) { return null; } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(conn.getZeroDateTimeBehavior())) { throw new SQLException( "Value '0000-00-00' can not be represented as java.sql.Timestamp", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } year = 1; month = 1; day = 1; } if (!rs.useLegacyDatetimeCode) { return TimeUtil.fastTimestampCreate(tz, year, month, day, hour, minute, seconds, nanos); } Timestamp ts = rs.fastTimestampCreate(rs .getCalendarInstanceForSessionOrNew(), year, month, day, hour, minute, seconds, nanos); Timestamp adjustedTs = TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, ts, conn .getServerTimezoneTZ(), tz, rollForward); return adjustedTs; } return rs.getNativeTimestampViaParseConversion(columnIndex + 1, targetCalendar, tz, rollForward); default: throw new SQLException( "Internal error - conversion method doesn't support this type", SQLError.SQL_STATE_GENERAL_ERROR); } } public abstract Object getNativeDateTimeValue(int columnIndex, Calendar targetCalendar, int jdbcType, int mysqlType, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) throws SQLException; protected double getNativeDouble(byte[] bits, int offset) { long valueAsLong = (bits[offset + 0] & 0xff) | ((long) (bits[offset + 1] & 0xff) << 8) | ((long) (bits[offset + 2] & 0xff) << 16) | ((long) (bits[offset + 3] & 0xff) << 24) | ((long) (bits[offset + 4] & 0xff) << 32) | ((long) (bits[offset + 5] & 0xff) << 40) | ((long) (bits[offset + 6] & 0xff) << 48) | ((long) (bits[offset + 7] & 0xff) << 56); return Double.longBitsToDouble(valueAsLong); } public abstract double getNativeDouble(int columnIndex) throws SQLException; protected float getNativeFloat(byte[] bits, int offset) { int asInt = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8) | ((bits[offset + 2] & 0xff) << 16) | ((bits[offset + 3] & 0xff) << 24); return Float.intBitsToFloat(asInt); } public abstract float getNativeFloat(int columnIndex) throws SQLException; protected int getNativeInt(byte[] bits, int offset) { int valueAsInt = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8) | ((bits[offset + 2] & 0xff) << 16) | ((bits[offset + 3] & 0xff) << 24); return valueAsInt; } public abstract int getNativeInt(int columnIndex) throws SQLException; protected long getNativeLong(byte[] bits, int offset) { long valueAsLong = (bits[offset + 0] & 0xff) | ((long) (bits[offset + 1] & 0xff) << 8) | ((long) (bits[offset + 2] & 0xff) << 16) | ((long) (bits[offset + 3] & 0xff) << 24) | ((long) (bits[offset + 4] & 0xff) << 32) | ((long) (bits[offset + 5] & 0xff) << 40) | ((long) (bits[offset + 6] & 0xff) << 48) | ((long) (bits[offset + 7] & 0xff) << 56); return valueAsLong; } public abstract long getNativeLong(int columnIndex) throws SQLException; protected short getNativeShort(byte[] bits, int offset) { short asShort = (short) ((bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8)); return asShort; } public abstract short getNativeShort(int columnIndex) throws SQLException; /** * * @param columnIndex * @param bits * @param offset * @param length * @param targetCalendar * @param tz * @param rollForward * @param conn * @param rs * @return * @throws SQLException */ protected Time getNativeTime(int columnIndex, byte[] bits, int offset, int length, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) throws SQLException { int hour = 0; int minute = 0; int seconds = 0; if (length != 0) { // bits[0] // skip tm->neg // binaryData.readLong(); // skip daysPart hour = bits[offset + 5]; minute = bits[offset + 6]; seconds = bits[offset + 7]; } if (!rs.useLegacyDatetimeCode) { return TimeUtil.fastTimeCreate(hour, minute, seconds, targetCalendar, this.exceptionInterceptor); } Calendar sessionCalendar = rs.getCalendarInstanceForSessionOrNew(); synchronized (sessionCalendar) { Time time = TimeUtil.fastTimeCreate(sessionCalendar, hour, minute, seconds, this.exceptionInterceptor); Time adjustedTime = TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, time, conn.getServerTimezoneTZ(), tz, rollForward); return adjustedTime; } } public abstract Time getNativeTime(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) throws SQLException; protected Timestamp getNativeTimestamp(byte[] bits, int offset, int length, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) throws SQLException { int year = 0; int month = 0; int day = 0; int hour = 0; int minute = 0; int seconds = 0; int nanos = 0; if (length != 0) { year = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8); month = bits[offset + 2]; day = bits[offset + 3]; if (length > 4) { hour = bits[offset + 4]; minute = bits[offset + 5]; seconds = bits[offset + 6]; } if (length > 7) { // MySQL uses microseconds nanos = ((bits[offset + 7] & 0xff) | ((bits[offset + 8] & 0xff) << 8) | ((bits[offset + 9] & 0xff) << 16) | ((bits[offset + 10] & 0xff) << 24)) * 1000; } } if (length == 0 || ((year == 0) && (month == 0) && (day == 0))) { if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(conn.getZeroDateTimeBehavior())) { return null; } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(conn.getZeroDateTimeBehavior())) { throw SQLError .createSQLException( "Value '0000-00-00' can not be represented as java.sql.Timestamp", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); } year = 1; month = 1; day = 1; } if (!rs.useLegacyDatetimeCode) { return TimeUtil.fastTimestampCreate(tz, year, month, day, hour, minute, seconds, nanos); } Calendar sessionCalendar = conn.getUseJDBCCompliantTimezoneShift() ? conn .getUtcCalendar() : rs.getCalendarInstanceForSessionOrNew(); synchronized (sessionCalendar) { Timestamp ts = rs.fastTimestampCreate(sessionCalendar, year, month, day, hour, minute, seconds, nanos); Timestamp adjustedTs = TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, ts, conn .getServerTimezoneTZ(), tz, rollForward); return adjustedTs; } } public abstract Timestamp getNativeTimestamp(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) throws SQLException; public abstract Reader getReader(int columnIndex) throws SQLException; /** * Returns the value at the given column (index starts at 0) as a * java.lang.String with the requested encoding, using the given * MySQLConnection to find character converters. * * @param index * of the column value (starting at 0) to return. * @param encoding * the Java name for the character encoding * @param conn * the connection that created this result set row * * @return the value for the given column (including NULL if it is) as a * String * * @throws SQLException * if an error occurs while retrieving the value. */ public abstract String getString(int index, String encoding, MySQLConnection conn) throws SQLException; /** * Convenience method for turning a byte[] into a string with the given * encoding. * * @param encoding * the Java encoding name for the byte[] -> char conversion * @param conn * the MySQLConnection that created the result set * @param value * the String value as a series of bytes, encoded using * "encoding" * @param offset * where to start the decoding * @param length * how many bytes to decode * * @return the String as decoded from bytes with the given encoding * * @throws SQLException * if an error occurs */ protected String getString(String encoding, MySQLConnection conn, byte[] value, int offset, int length) throws SQLException { String stringVal = null; if ((conn != null) && conn.getUseUnicode()) { try { if (encoding == null) { stringVal = StringUtils.toString(value); } else { SingleByteCharsetConverter converter = conn .getCharsetConverter(encoding); if (converter != null) { stringVal = converter.toString(value, offset, length); } else { stringVal = StringUtils.toString(value, offset, length, encoding); } } } catch (java.io.UnsupportedEncodingException E) { throw SQLError .createSQLException( Messages .getString("ResultSet.Unsupported_character_encoding____101") //$NON-NLS-1$ + encoding + "'.", "0S100", this.exceptionInterceptor); } } else { stringVal = StringUtils.toAsciiString(value, offset, length); } return stringVal; } protected Time getTimeFast(int columnIndex, byte[] timeAsBytes, int offset, int fullLength, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) throws SQLException { int hr = 0; int min = 0; int sec = 0; int nanos = 0; int decimalIndex = -1; try { if (timeAsBytes == null) { return null; } boolean allZeroTime = true; boolean onlyTimePresent = false; for (int i = 0; i < fullLength; i++) { if (timeAsBytes[offset + i] == ':') { onlyTimePresent = true; break; } } for (int i = 0; i < fullLength; i++) { if (timeAsBytes[offset + i] == '.') { decimalIndex = i; break; } } for (int i = 0; i < fullLength; i++) { byte b = timeAsBytes[offset + i]; if (b == ' ' || b == '-' || b == '/') { onlyTimePresent = false; } if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' && b != '.') { allZeroTime = false; break; } } if (!onlyTimePresent && allZeroTime) { if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(conn.getZeroDateTimeBehavior())) { return null; } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(conn.getZeroDateTimeBehavior())) { throw SQLError.createSQLException("Value '" + StringUtils.toString(timeAsBytes) + "' can not be represented as java.sql.Time", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); } // We're left with the case of 'round' to a time Java _can_ // represent, which is '00:00:00' return rs.fastTimeCreate(targetCalendar, 0, 0, 0); } Field timeColField = this.metadata[columnIndex]; int length = fullLength; if (decimalIndex != -1) { length = decimalIndex; if ((decimalIndex + 2) <= fullLength) { nanos = StringUtils.getInt(timeAsBytes, offset + decimalIndex + 1, offset + fullLength); int numDigits = (fullLength) - (decimalIndex + 1); if (numDigits < 9) { int factor = (int)(Math.pow(10, 9 - numDigits)); nanos = nanos * factor; } } else { throw new IllegalArgumentException(); // re-thrown // further // down // with // a // much better error message } } if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { switch (length) { case 19: { // YYYY-MM-DD hh:mm:ss hr = StringUtils.getInt(timeAsBytes, offset + length - 8, offset + length - 6); min = StringUtils.getInt(timeAsBytes, offset + length - 5, offset + length - 3); sec = StringUtils.getInt(timeAsBytes, offset + length - 2, offset + length); } break; case 14: case 12: { hr = StringUtils.getInt(timeAsBytes, offset + length - 6, offset + length - 4); min = StringUtils.getInt(timeAsBytes, offset + length - 4, offset + length - 2); sec = StringUtils.getInt(timeAsBytes, offset + length - 2, offset + length); } break; case 10: { hr = StringUtils .getInt(timeAsBytes, offset + 6, offset + 8); min = StringUtils.getInt(timeAsBytes, offset + 8, offset + 10); sec = 0; } break; default: throw SQLError .createSQLException( Messages .getString("ResultSet.Timestamp_too_small_to_convert_to_Time_value_in_column__257") //$NON-NLS-1$ + (columnIndex + 1) + "(" + timeColField + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); } /* endswitch */ @SuppressWarnings("unused") SQLWarning precisionLost = new SQLWarning( Messages .getString("ResultSet.Precision_lost_converting_TIMESTAMP_to_Time_with_getTime()_on_column__261") //$NON-NLS-1$ + columnIndex + "(" + timeColField + ")."); /* * if (this.warningChain == null) { this.warningChain = * precisionLost; } else { * this.warningChain.setNextWarning(precisionLost); } */ } else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATETIME) { hr = StringUtils.getInt(timeAsBytes, offset + 11, offset + 13); min = StringUtils.getInt(timeAsBytes, offset + 14, offset + 16); sec = StringUtils.getInt(timeAsBytes, offset + 17, offset + 19); @SuppressWarnings("unused") SQLWarning precisionLost = new SQLWarning( Messages .getString("ResultSet.Precision_lost_converting_DATETIME_to_Time_with_getTime()_on_column__264") //$NON-NLS-1$ + (columnIndex + 1) + "(" + timeColField + ")."); /* * if (this.warningChain == null) { this.warningChain = * precisionLost; } else { * this.warningChain.setNextWarning(precisionLost); } */ } else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) { return rs.fastTimeCreate(null, 0, 0, 0); // midnight on the // given // date } else { // convert a String to a Time if ((length != 5) && (length != 8)) { throw SQLError.createSQLException(Messages .getString("ResultSet.Bad_format_for_Time____267") //$NON-NLS-1$ + StringUtils.toString(timeAsBytes) + Messages.getString("ResultSet.___in_column__268") + (columnIndex + 1), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); } hr = StringUtils.getInt(timeAsBytes, offset + 0, offset + 2); min = StringUtils.getInt(timeAsBytes, offset + 3, offset + 5); sec = (length == 5) ? 0 : StringUtils.getInt(timeAsBytes, offset + 6, offset + 8); } Calendar sessionCalendar = rs.getCalendarInstanceForSessionOrNew(); if (!rs.useLegacyDatetimeCode) { // TODO: return rs.fastTimeCreate(targetCalendar, hr, min, sec, nanos); // java.sql.Time doesn't contain fractional part, so PreparedStatement.setTime/getTime can't deal with TIME(n) fractional part. // There may be better mappings to high-precision time coming in JDBC-5 with the adoption of JSR-310. return rs.fastTimeCreate(targetCalendar, hr, min, sec); } synchronized (sessionCalendar) { return TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, rs.fastTimeCreate(sessionCalendar, hr, min, sec), conn.getServerTimezoneTZ(), tz, rollForward); // TODO: min, sec, nanos), conn.getServerTimezoneTZ(), tz, // java.sql.Time doesn't contain fractional part, so PreparedStatement.setTime/getTime can't deal with TIME(n) fractional part. // There may be better mappings to high-precision time coming in JDBC-5 with the adoption of JSR-310. } } catch (RuntimeException ex) { SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); sqlEx.initCause(ex); throw sqlEx; } } public abstract Time getTimeFast(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) throws SQLException; protected Timestamp getTimestampFast(int columnIndex, byte[] timestampAsBytes, int offset, int length, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) throws SQLException { try { Calendar sessionCalendar = conn.getUseJDBCCompliantTimezoneShift() ? conn .getUtcCalendar() : rs.getCalendarInstanceForSessionOrNew(); synchronized (sessionCalendar) { boolean allZeroTimestamp = true; boolean onlyTimePresent = false; for (int i = 0; i < length; i++) { if (timestampAsBytes[offset + i] == ':') { onlyTimePresent = true; break; } } for (int i = 0; i < length; i++) { byte b = timestampAsBytes[offset + i]; if (b == ' ' || b == '-' || b == '/') { onlyTimePresent = false; } if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' && b != '.') { allZeroTimestamp = false; break; } } if (!onlyTimePresent && allZeroTimestamp) { if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL .equals(conn.getZeroDateTimeBehavior())) { return null; } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION .equals(conn.getZeroDateTimeBehavior())) { throw SQLError .createSQLException( "Value '" + StringUtils.toString(timestampAsBytes) + "' can not be represented as java.sql.Timestamp", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); } if (!rs.useLegacyDatetimeCode) { return TimeUtil.fastTimestampCreate(tz, 1, 1, 1, 0, 0, 0, 0); } // We're left with the case of 'round' to a date Java _can_ // represent, which is '0001-01-01'. return rs.fastTimestampCreate(null, 1, 1, 1, 0, 0, 0, 0); } else if (this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { if (!rs.useLegacyDatetimeCode) { return TimeUtil.fastTimestampCreate(tz, StringUtils .getInt(timestampAsBytes, offset, 4), 1, 1, 0, 0, 0, 0); } return TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, rs.fastTimestampCreate( sessionCalendar, StringUtils.getInt( timestampAsBytes, offset, 4), 1, 1, 0, 0, 0, 0), conn.getServerTimezoneTZ(), tz, rollForward); } else { if (timestampAsBytes[offset + length - 1] == '.') { length--; } // Convert from TIMESTAMP or DATE int year = 0; int month = 0; int day = 0; int hour = 0; int minutes = 0; int seconds = 0; int nanos = 0; switch (length) { case 29: case 26: case 25: case 24: case 23: case 22: case 21: case 20: case 19: { year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 4); month = StringUtils.getInt(timestampAsBytes, offset + 5, offset + 7); day = StringUtils.getInt(timestampAsBytes, offset + 8, offset + 10); hour = StringUtils.getInt(timestampAsBytes, offset + 11, offset + 13); minutes = StringUtils.getInt(timestampAsBytes, offset + 14, offset + 16); seconds = StringUtils.getInt(timestampAsBytes, offset + 17, offset + 19); nanos = 0; if (length > 19) { int decimalIndex = -1; for (int i = 0; i < length; i++) { if (timestampAsBytes[offset + i] == '.') { decimalIndex = i; } } if (decimalIndex != -1) { if ((decimalIndex + 2) <= length) { nanos = StringUtils.getInt( timestampAsBytes, offset + decimalIndex + 1, offset + length); int numDigits = (length) - (decimalIndex + 1); if (numDigits < 9) { int factor = (int)(Math.pow(10, 9 - numDigits)); nanos = nanos * factor; } } else { throw new IllegalArgumentException(); // re-thrown // further // down // with // a // much better error message } } } break; } case 14: { year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 4); month = StringUtils.getInt(timestampAsBytes, offset + 4, offset + 6); day = StringUtils.getInt(timestampAsBytes, offset + 6, offset + 8); hour = StringUtils.getInt(timestampAsBytes, offset + 8, offset + 10); minutes = StringUtils.getInt(timestampAsBytes, offset + 10, offset + 12); seconds = StringUtils.getInt(timestampAsBytes, offset + 12, offset + 14); break; } case 12: { year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 2); if (year <= 69) { year = (year + 100); } year += 1900; month = StringUtils.getInt(timestampAsBytes, offset + 2, offset + 4); day = StringUtils.getInt(timestampAsBytes, offset + 4, offset + 6); hour = StringUtils.getInt(timestampAsBytes, offset + 6, offset + 8); minutes = StringUtils.getInt(timestampAsBytes, offset + 8, offset + 10); seconds = StringUtils.getInt(timestampAsBytes, offset + 10, offset + 12); break; } case 10: { boolean hasDash = false; for (int i = 0; i < length; i++) { if (timestampAsBytes[offset + i] == '-') { hasDash = true; break; } } if ((this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) || hasDash) { year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 4); month = StringUtils.getInt(timestampAsBytes, offset + 5, offset + 7); day = StringUtils.getInt(timestampAsBytes, offset + 8, offset + 10); hour = 0; minutes = 0; } else { year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 2); if (year <= 69) { year = (year + 100); } month = StringUtils.getInt(timestampAsBytes, offset + 2, offset + 4); day = StringUtils.getInt(timestampAsBytes, offset + 4, offset + 6); hour = StringUtils.getInt(timestampAsBytes, offset + 6, offset + 8); minutes = StringUtils.getInt(timestampAsBytes, offset + 8, offset + 10); year += 1900; // two-digit year } break; } case 8: { boolean hasColon = false; for (int i = 0; i < length; i++) { if (timestampAsBytes[offset + i] == ':') { hasColon = true; break; } } if (hasColon) { hour = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 2); minutes = StringUtils.getInt(timestampAsBytes, offset + 3, offset + 5); seconds = StringUtils.getInt(timestampAsBytes, offset + 6, offset + 8); year = 1970; month = 1; day = 1; break; } year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 4); month = StringUtils.getInt(timestampAsBytes, offset + 4, offset + 6); day = StringUtils.getInt(timestampAsBytes, offset + 6, offset + 8); year -= 1900; month--; break; } case 6: { year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 2); if (year <= 69) { year = (year + 100); } year += 1900; month = StringUtils.getInt(timestampAsBytes, offset + 2, offset + 4); day = StringUtils.getInt(timestampAsBytes, offset + 4, offset + 6); break; } case 4: { year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 2); if (year <= 69) { year = (year + 100); } month = StringUtils.getInt(timestampAsBytes, offset + 2, offset + 4); day = 1; break; } case 2: { year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 2); if (year <= 69) { year = (year + 100); } year += 1900; month = 1; day = 1; break; } default: throw new java.sql.SQLException( "Bad format for Timestamp '" + StringUtils.toString(timestampAsBytes) + "' in column " + (columnIndex + 1) + ".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } if (!rs.useLegacyDatetimeCode) { return TimeUtil.fastTimestampCreate(tz, year, month, day, hour, minutes, seconds, nanos); } return TimeUtil .changeTimezone(conn, sessionCalendar, targetCalendar, rs.fastTimestampCreate( sessionCalendar, year, month, day, hour, minutes, seconds, nanos), conn .getServerTimezoneTZ(), tz, rollForward); } } } catch (RuntimeException e) { SQLException sqlEx = SQLError.createSQLException("Cannot convert value '" + getString(columnIndex, "ISO8859_1", conn) + "' from column " + (columnIndex + 1) + " to TIMESTAMP.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); sqlEx.initCause(e); throw sqlEx; } } public abstract Timestamp getTimestampFast(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) throws SQLException; /** * Could the column value at the given index (which starts at 0) be * interpreted as a floating-point number (has +/-/E/e in it)? * * @param index * of the column value (starting at 0) to check. * * @return true if the column value at the given index looks like it might * be a floating-point number, false if not. * * @throws SQLException * if an error occurs */ public abstract boolean isFloatingPointNumber(int index) throws SQLException; /** * Is the column value at the given index (which starts at 0) NULL? * * @param index * of the column value (starting at 0) to check. * * @return true if the column value is NULL, false if not. * * @throws SQLException * if an error occurs */ public abstract boolean isNull(int index) throws SQLException; /** * Returns the length of the column at the given index (which starts at 0). * * @param index * of the column value (starting at 0) for which to return the * length. * @return the length of the requested column, 0 if null (clients of this * interface should use isNull() beforehand to determine status of * NULL values in the column). * * @throws SQLException */ public abstract long length(int index) throws SQLException; /** * Sets the given column value (only works currently with * ByteArrayRowHolder). * * @param index * index of the column value (starting at 0) to set. * @param value * the (raw) value to set * * @throws SQLException * if an error occurs, or the concrete RowHolder doesn't support * this operation. */ public abstract void setColumnValue(int index, byte[] value) throws SQLException; public ResultSetRow setMetadata(Field[] f) throws SQLException { this.metadata = f; return this; } public abstract int getBytesSize(); }