Skip to content

Commit

Permalink
Fix for Bug#72609 (18749544), SETDATE() NOT USING A PROLEPTIC GREGORIAN
Browse files Browse the repository at this point in the history
CALENDAR.
  • Loading branch information
soklakov committed Jul 27, 2018
1 parent 614b595 commit d2d71a7
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 94 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

Version 5.1.47

- Fix for Bug#72609 (18749544), SETDATE() NOT USING A PROLEPTIC GREGORIAN CALENDAR.

- Fix for Bug#87534 (26730196), UNION ALL query fails when useServerPrepStmts=true on database connection.
Test case only. Base bug fixed in MySQL 5.7.22.

Expand Down
4 changes: 1 addition & 3 deletions src/com/mysql/jdbc/EscapeProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -376,9 +376,7 @@ private static void processTimestampToken(MySQLConnection conn, StringBuilder ne

ts = TimeUtil.adjustTimestampNanosPrecision(ts, 6, !conn.isServerTruncatesFracSecs());

SimpleDateFormat tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss", Locale.US);

tsdf.setTimeZone(conn.getServerTimezoneTZ());
SimpleDateFormat tsdf = TimeUtil.getSimpleDateFormat(null, "''yyyy-MM-dd HH:mm:ss", null, conn.getServerTimezoneTZ());

newSql.append(tsdf.format(ts));

Expand Down
70 changes: 26 additions & 44 deletions src/com/mysql/jdbc/PreparedStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
Expand Down Expand Up @@ -1809,7 +1808,7 @@ protected long[] executeBatchSerially(int batchTimeout) throws SQLException {
}

public String getDateTime(String pattern) {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
SimpleDateFormat sdf = TimeUtil.getSimpleDateFormat(null, pattern, null, null);
return sdf.format(new java.util.Date());
}

Expand Down Expand Up @@ -3227,12 +3226,7 @@ public void setDate(int parameterIndex, java.sql.Date x, Calendar cal) throws SQ
newSetDateInternal(parameterIndex, x, cal);
} else {
synchronized (checkClosed().getConnectionMutex()) {
if (this.ddf == null) {
this.ddf = new SimpleDateFormat("''yyyy-MM-dd''", Locale.US);
}
if (cal != null) {
this.ddf.setTimeZone(cal.getTimeZone());
}
this.ddf = TimeUtil.getSimpleDateFormat(this.ddf, "''yyyy-MM-dd''", cal, null);

setInternal(parameterIndex, this.ddf.format(x));

Expand Down Expand Up @@ -3700,7 +3694,8 @@ public void setObject(int parameterIndex, Object parameterObj, int targetSqlType

if (parameterObj instanceof String) {
ParsePosition pp = new ParsePosition(0);
java.text.DateFormat sdf = new java.text.SimpleDateFormat(getDateTimePattern((String) parameterObj, false), Locale.US);
// TODO set proleptic if needed ?
java.text.DateFormat sdf = TimeUtil.getSimpleDateFormat(null, getDateTimePattern((String) parameterObj, false), null, null);
parameterAsDate = sdf.parse((String) parameterObj, pp);
} else {
parameterAsDate = (java.util.Date) parameterObj;
Expand Down Expand Up @@ -3733,7 +3728,7 @@ public void setObject(int parameterIndex, Object parameterObj, int targetSqlType
case Types.TIME:

if (parameterObj instanceof String) {
java.text.DateFormat sdf = new java.text.SimpleDateFormat(getDateTimePattern((String) parameterObj, true), Locale.US);
java.text.DateFormat sdf = TimeUtil.getSimpleDateFormat(null, getDateTimePattern((String) parameterObj, true), null, null);
setTime(parameterIndex, new java.sql.Time(sdf.parse((String) parameterObj).getTime()));
} else if (parameterObj instanceof Timestamp) {
Timestamp xT = (Timestamp) parameterObj;
Expand Down Expand Up @@ -4246,14 +4241,21 @@ protected void setTimestampInternal(int parameterIndex, Timestamp x, Calendar ta
Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ? this.connection.getUtcCalendar()
: getCalendarInstanceForSessionOrNew();

// check if proleptic calendar is needed
sessionCalendar = TimeUtil.setProlepticIfNeeded(sessionCalendar, targetCalendar);

x = TimeUtil.changeTimezone(this.connection, sessionCalendar, targetCalendar, x, tz, this.connection.getServerTimezoneTZ(), rollForward);

if (useSSPSCompatibleTimezoneShift) {
doSSPSCompatibleTimezoneShift(parameterIndex, x, fractionalLength);
doSSPSCompatibleTimezoneShift(parameterIndex, x, fractionalLength, targetCalendar);
} else {
synchronized (this) {
if (this.tsdf == null) {
this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss", Locale.US);

this.tsdf = TimeUtil.getSimpleDateFormat(this.tsdf, "''yyyy-MM-dd HH:mm:ss", null, null);

Calendar adjCal = TimeUtil.setProlepticIfNeeded(this.tsdf.getCalendar(), targetCalendar);
if (this.tsdf.getCalendar() != adjCal) {
this.tsdf.setCalendar(adjCal);
}

StringBuffer buf = new StringBuffer();
Expand Down Expand Up @@ -4282,15 +4284,8 @@ protected void setTimestampInternal(int parameterIndex, Timestamp x, Calendar ta

private void newSetTimestampInternal(int parameterIndex, Timestamp x, Calendar targetCalendar) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (this.tsdf == null) {
this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss", Locale.US);
}

if (targetCalendar != null) {
this.tsdf.setTimeZone(targetCalendar.getTimeZone());
} else {
this.tsdf.setTimeZone(this.connection.getServerTimezoneTZ());
}
this.tsdf = TimeUtil.getSimpleDateFormat(this.tsdf, "''yyyy-MM-dd HH:mm:ss", targetCalendar,
targetCalendar != null ? null : this.connection.getServerTimezoneTZ());

StringBuffer buf = new StringBuffer();
buf.append(this.tsdf.format(x));
Expand All @@ -4304,43 +4299,30 @@ private void newSetTimestampInternal(int parameterIndex, Timestamp x, Calendar t

private void newSetTimeInternal(int parameterIndex, Time x, Calendar targetCalendar) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (this.tdf == null) {
this.tdf = new SimpleDateFormat("''HH:mm:ss''", Locale.US);
}

if (targetCalendar != null) {
this.tdf.setTimeZone(targetCalendar.getTimeZone());
} else {
this.tdf.setTimeZone(this.connection.getServerTimezoneTZ());
}
this.tdf = TimeUtil.getSimpleDateFormat(this.tdf, "''HH:mm:ss''", targetCalendar,
targetCalendar != null ? null : this.connection.getServerTimezoneTZ());

setInternal(parameterIndex, this.tdf.format(x));
}
}

private void newSetDateInternal(int parameterIndex, Date x, Calendar targetCalendar) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (this.ddf == null) {
this.ddf = new SimpleDateFormat("''yyyy-MM-dd''", Locale.US);
}

if (targetCalendar != null) {
this.ddf.setTimeZone(targetCalendar.getTimeZone());
} else if (this.connection.getNoTimezoneConversionForDateType()) {
this.ddf.setTimeZone(this.connection.getDefaultTimeZone());
} else {
this.ddf.setTimeZone(this.connection.getServerTimezoneTZ());
}
this.ddf = TimeUtil.getSimpleDateFormat(this.ddf, "''yyyy-MM-dd''", targetCalendar, targetCalendar != null ? null
: (this.connection.getNoTimezoneConversionForDateType() ? this.connection.getDefaultTimeZone() : this.connection.getServerTimezoneTZ()));

setInternal(parameterIndex, this.ddf.format(x));
}
}

private void doSSPSCompatibleTimezoneShift(int parameterIndex, Timestamp x, int fractionalLength) throws SQLException {
private void doSSPSCompatibleTimezoneShift(int parameterIndex, Timestamp x, int fractionalLength, Calendar targetCalendar) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
Calendar sessionCalendar2 = (this.connection.getUseJDBCCompliantTimezoneShift()) ? this.connection.getUtcCalendar()
Calendar sessionCalendar2 = this.connection.getUseJDBCCompliantTimezoneShift() ? this.connection.getUtcCalendar()
: getCalendarInstanceForSessionOrNew();

// check if proleptic calendar is needed
sessionCalendar2 = TimeUtil.setProlepticIfNeeded(sessionCalendar2, targetCalendar);

synchronized (sessionCalendar2) {
java.util.Date oldTime = sessionCalendar2.getTime();

Expand Down
1 change: 1 addition & 0 deletions src/com/mysql/jdbc/ResultSetRow.java
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,7 @@ protected Timestamp getTimestampFast(int columnIndex, byte[] timestampAsBytes, i

try {
Calendar sessionCalendar = useJDBCCompliantTimezoneShift ? conn.getUtcCalendar() : rs.getCalendarInstanceForSessionOrNew();
sessionCalendar = TimeUtil.setProlepticIfNeeded(sessionCalendar, targetCalendar);

boolean allZeroTimestamp = true;

Expand Down
44 changes: 31 additions & 13 deletions src/com/mysql/jdbc/ServerPreparedStatement.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2002, 2018, 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.
Expand Down Expand Up @@ -114,6 +114,8 @@ public static class BindValue {

public Object value; /* The value to store */

public Calendar calendar; /* Calendar to be used for DATE and DATETIME values storing */

BindValue() {
}

Expand All @@ -127,6 +129,7 @@ public static class BindValue {
this.longBinding = copyMe.longBinding;
this.floatBinding = copyMe.floatBinding;
this.doubleBinding = copyMe.doubleBinding;
this.calendar = copyMe.calendar;
}

void reset() {
Expand All @@ -138,6 +141,8 @@ void reset() {
this.longBinding = 0L;
this.floatBinding = 0;
this.doubleBinding = 0D;

this.calendar = null;
}

@Override
Expand Down Expand Up @@ -1854,6 +1859,9 @@ public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLExceptio
resetToType(binding, MysqlDefs.FIELD_TYPE_DATE);

binding.value = x;
if (cal != null) {
binding.calendar = (Calendar) cal.clone();
}
}
}

Expand Down Expand Up @@ -2044,6 +2052,9 @@ private void setTimeInternal(int parameterIndex, java.sql.Time x, Calendar targe

if (!this.useLegacyDatetimeCode) {
binding.value = x;
if (targetCalendar != null) {
binding.calendar = (Calendar) targetCalendar.clone();
}
} else {
Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();

Expand Down Expand Up @@ -2109,10 +2120,15 @@ private void setTimestampInternal(int parameterIndex, java.sql.Timestamp x, Cale
} else {
Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ? this.connection.getUtcCalendar()
: getCalendarInstanceForSessionOrNew();
// check if proleptic calendar is needed
sessionCalendar = TimeUtil.setProlepticIfNeeded(sessionCalendar, targetCalendar);

binding.value = TimeUtil.changeTimezone(this.connection, sessionCalendar, targetCalendar, x, tz, this.connection.getServerTimezoneTZ(),
rollForward);
}
if (targetCalendar != null) {
binding.calendar = (Calendar) targetCalendar.clone();
}
}
}

Expand Down Expand Up @@ -2214,7 +2230,7 @@ private void storeBinding(Buffer packet, BindValue bindValue, MysqlIO mysql) thr
case MysqlDefs.FIELD_TYPE_DATE:
case MysqlDefs.FIELD_TYPE_DATETIME:
case MysqlDefs.FIELD_TYPE_TIMESTAMP:
storeDateTime(packet, (java.util.Date) value, mysql, bindValue.bufferType);
storeDateTime(packet, (java.util.Date) value, mysql, bindValue.bufferType, bindValue.calendar);
return;
case MysqlDefs.FIELD_TYPE_VAR_STRING:
case MysqlDefs.FIELD_TYPE_STRING:
Expand Down Expand Up @@ -2293,29 +2309,31 @@ private void storeDateTime412AndOlder(Buffer intoBuf, java.util.Date dt, int buf
* @param bufferType
* @throws SQLException
*/
private void storeDateTime(Buffer intoBuf, java.util.Date dt, MysqlIO mysql, int bufferType) throws SQLException {
private void storeDateTime(Buffer intoBuf, java.util.Date dt, MysqlIO mysql, int bufferType, Calendar cal) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (this.connection.versionMeetsMinimum(4, 1, 3)) {
storeDateTime413AndNewer(intoBuf, dt, bufferType);
storeDateTime413AndNewer(intoBuf, dt, bufferType, cal);
} else {
storeDateTime412AndOlder(intoBuf, dt, bufferType);
}
}
}

private void storeDateTime413AndNewer(Buffer intoBuf, java.util.Date dt, int bufferType) throws SQLException {
private void storeDateTime413AndNewer(Buffer intoBuf, java.util.Date dt, int bufferType, Calendar cal) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
Calendar sessionCalendar = null;
Calendar sessionCalendar = cal;

if (!this.useLegacyDatetimeCode) {
if (bufferType == MysqlDefs.FIELD_TYPE_DATE) {
sessionCalendar = getDefaultTzCalendar();
if (cal == null) {
if (!this.useLegacyDatetimeCode) {
if (bufferType == MysqlDefs.FIELD_TYPE_DATE) {
sessionCalendar = getDefaultTzCalendar();
} else {
sessionCalendar = getServerTzCalendar();
}
} else {
sessionCalendar = getServerTzCalendar();
sessionCalendar = (dt instanceof Timestamp && this.connection.getUseJDBCCompliantTimezoneShift()) ? this.connection.getUtcCalendar()
: getCalendarInstanceForSessionOrNew();
}
} else {
sessionCalendar = (dt instanceof Timestamp && this.connection.getUseJDBCCompliantTimezoneShift()) ? this.connection.getUtcCalendar()
: getCalendarInstanceForSessionOrNew();
}

java.util.Date oldTime = sessionCalendar.getTime();
Expand Down
35 changes: 35 additions & 0 deletions src/com/mysql/jdbc/TimeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Properties;
import java.util.TimeZone;

Expand Down Expand Up @@ -595,4 +597,37 @@ public static Timestamp truncateFractionalSeconds(Timestamp timestamp) {
truncatedTimestamp.setNanos(0);
return truncatedTimestamp;
}

public static SimpleDateFormat getSimpleDateFormat(SimpleDateFormat cachedSimpleDateFormat, String pattern, Calendar cal, TimeZone tz) {
SimpleDateFormat sdf = cachedSimpleDateFormat != null ? cachedSimpleDateFormat : new SimpleDateFormat(pattern, Locale.US);

if (cal != null) {
sdf.setCalendar((Calendar) cal.clone()); // cloning the original calendar to avoid it's modification
}

if (tz != null) {
sdf.setTimeZone(tz);
}
return sdf;
}

/**
* Return the proleptic version of origCalendar if refCalendar is proleptic. Applied only to GregorianCalendar parameters.
*
* @param origCalendar
* original Calendar
* @param refCalendar
* reference Calendar
* @return the original Calendar if no adjustments are needed or the new proleptic GregorianCalendar with preserved Timezone of origCalendar and other
* fields unset.
*/
public static Calendar setProlepticIfNeeded(Calendar origCalendar, Calendar refCalendar) {
if (origCalendar != null && refCalendar != null && origCalendar instanceof GregorianCalendar && refCalendar instanceof GregorianCalendar
&& ((GregorianCalendar) refCalendar).getGregorianChange().getTime() == Long.MIN_VALUE) {
origCalendar = (GregorianCalendar) origCalendar.clone();
((GregorianCalendar) origCalendar).setGregorianChange(new Date(Long.MIN_VALUE));
origCalendar.clear();
}
return origCalendar;
}
}
4 changes: 2 additions & 2 deletions src/testsuite/regression/ConnectionRegressionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7404,9 +7404,9 @@ private void testBug71084AssertCase(Properties connProps, String clientTZ, Strin
try {
TimeZone.setDefault(TimeZone.getTimeZone(clientTZ));

SimpleDateFormat longDateFrmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat longDateFrmt = TimeUtil.getSimpleDateFormat(null, "yyyy-MM-dd HH:mm:ss", null, null);
longDateFrmt.setTimeZone(TimeZone.getDefault());
SimpleDateFormat shortDateFrmt = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat shortDateFrmt = TimeUtil.getSimpleDateFormat(null, "yyyy-MM-dd", null, null);
shortDateFrmt.setTimeZone(TimeZone.getDefault());

Calendar targetCal = null;
Expand Down
Loading

0 comments on commit d2d71a7

Please sign in to comment.