Skip to content

Commit

Permalink
Fix for Bug#91112 (28125069), AGAIN WRONG JAVA.SQL.DATE.
Browse files Browse the repository at this point in the history
  • Loading branch information
soklakov committed Feb 10, 2020
1 parent ed4f635 commit c1e9c7d
Show file tree
Hide file tree
Showing 17 changed files with 230 additions and 63 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Expand Up @@ -3,6 +3,8 @@

Version 8.0.20

- Fix for Bug#91112 (28125069), AGAIN WRONG JAVA.SQL.DATE.

- Fix for Bug#30474158, CONNECTOR/J 8 DOES NOT HONOR THE REQUESTED RESULTSETTYPE SCROLL_INSENSITIVE ETC.

- Fix for Bug#98445 (30832513), Connection option clientInfoProvider=ClientInfoProviderSP causes NPE.
Expand Down
Expand Up @@ -369,6 +369,9 @@ public enum DatabaseTerm {
new StringPropertyDefinition(PropertyKey.queryInterceptors, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE,
Messages.getString("ConnectionProperties.queryInterceptors"), "8.0.7", CATEGORY_STATEMENTS, Integer.MIN_VALUE),

new BooleanPropertyDefinition(PropertyKey.cacheDefaultTimezone, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE,
Messages.getString("ConnectionProperties.cacheDefaultTimezone"), "8.0.20", CATEGORY_STATEMENTS, Integer.MIN_VALUE),

//
// CATEGORY_PREPARED_STATEMENTS
//
Expand Down
1 change: 1 addition & 0 deletions src/main/core-api/java/com/mysql/cj/conf/PropertyKey.java
Expand Up @@ -78,6 +78,7 @@ public enum PropertyKey {
blobsAreStrings("blobsAreStrings", true), //
blobSendChunkSize("blobSendChunkSize", true), //
cacheCallableStmts("cacheCallableStmts", true), //
cacheDefaultTimezone("cacheDefaultTimezone", true), //
cachePrepStmts("cachePrepStmts", true), //
cacheResultSetMetadata("cacheResultSetMetadata", true), //
cacheServerConfiguration("cacheServerConfiguration", true), //
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
Expand Down Expand Up @@ -251,9 +251,7 @@ public interface ServerSession {
* The default time zone used to marshall date/time values to/from the server. This is used when getDate(), etc methods are called without a calendar
* argument.
*
* @return The server time zone (which may be user overridden in a connection property)
* @return The default JVM time zone
*/
TimeZone getDefaultTimeZone();

void setDefaultTimeZone(TimeZone defaultTimeZone);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
Expand Down Expand Up @@ -728,7 +728,7 @@ public void setTime(int parameterIndex, Time x, Calendar cal) {
if (x == null) {
setNull(parameterIndex);
} else {
this.tdf = TimeUtil.getSimpleDateFormat(this.tdf, "''HH:mm:ss''", cal, cal != null ? null : this.session.getServerSession().getDefaultTimeZone());
this.tdf = TimeUtil.getSimpleDateFormat(this.tdf, "''HH:mm:ss''", cal, cal != null ? null : this.session.getServerSession().getServerTimeZone());
setValue(parameterIndex, this.tdf.format(x), MysqlType.TIME);
}
}
Expand Down Expand Up @@ -783,7 +783,7 @@ public void setTimestamp(int parameterIndex, Timestamp x, Calendar targetCalenda
x = TimeUtil.adjustTimestampNanosPrecision(x, fractionalLength, !this.session.getServerSession().isServerTruncatesFracSecs());

this.tsdf = TimeUtil.getSimpleDateFormat(this.tsdf, "''yyyy-MM-dd HH:mm:ss", targetCalendar,
targetCalendar != null ? null : this.session.getServerSession().getDefaultTimeZone());
targetCalendar != null ? null : this.session.getServerSession().getServerTimeZone());

StringBuffer buf = new StringBuffer();
buf.append(this.tsdf.format(x));
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
Expand Down Expand Up @@ -33,6 +33,9 @@
import java.util.Locale;
import java.util.TimeZone;

import com.mysql.cj.conf.PropertyKey;
import com.mysql.cj.conf.PropertySet;
import com.mysql.cj.conf.RuntimeProperty;
import com.mysql.cj.exceptions.CJException;
import com.mysql.cj.exceptions.ExceptionFactory;
import com.mysql.cj.exceptions.ExceptionInterceptor;
Expand All @@ -53,11 +56,15 @@ public class ServerPreparedQueryBindValue extends ClientPreparedQueryBindValue i
public Calendar calendar;

private TimeZone defaultTimeZone;
private TimeZone serverTimeZone;
private RuntimeProperty<Boolean> cacheDefaultTimezone = null;

protected String charEncoding = null;

public ServerPreparedQueryBindValue(TimeZone defaultTZ) {
public ServerPreparedQueryBindValue(TimeZone defaultTZ, TimeZone serverTZ, PropertySet pset) {
this.defaultTimeZone = defaultTZ;
this.serverTimeZone = serverTZ;
this.cacheDefaultTimezone = pset.getBooleanProperty(PropertyKey.cacheDefaultTimezone);
}

@Override
Expand All @@ -68,7 +75,7 @@ public ServerPreparedQueryBindValue clone() {
private ServerPreparedQueryBindValue(ServerPreparedQueryBindValue copyMe) {
super(copyMe);

This comment has been minimized.

Copy link
@ctamisier

ctamisier May 9, 2020

@soklakov
Here, when copyMe has cacheDefaultTimezone defined, this value is not set to the creation of the current ServerPreparedQueryBindValue object, and leads to a NPE at line 287.
I have this NPE but I'm not totally sure about the source. May it be this constructor missing cacheDefaultTimezone assignment.
I can raise an issue but I don't know where yet. Thanks !

This comment has been minimized.

Copy link
@soklakov

soklakov May 12, 2020

Author Contributor

@ctamisier
Please, report it on https://bugs.mysql.com/ in "Connector / J" category.
Thanks!

This comment has been minimized.

Copy link
@ctamisier

this.defaultTimeZone = copyMe.defaultTimeZone;
this.serverTimeZone = copyMe.serverTimeZone;
this.bufferType = copyMe.bufferType;
this.calendar = copyMe.calendar;
this.charEncoding = copyMe.charEncoding;
Expand Down Expand Up @@ -229,6 +236,8 @@ public void storeBinding(NativePacketPayload intoPacket, boolean isLoadDataQuery
storeTime(intoPacket);
return;
case MysqlType.FIELD_TYPE_DATE:
storeDate(intoPacket);
return;
case MysqlType.FIELD_TYPE_DATETIME:
case MysqlType.FIELD_TYPE_TIMESTAMP:
storeDateTime(intoPacket);
Expand Down Expand Up @@ -263,7 +272,7 @@ private void storeTime(NativePacketPayload intoPacket) {
intoPacket.writeInteger(IntegerDataType.INT4, 0); // tm->day, not used

if (this.calendar == null) {
this.calendar = Calendar.getInstance(this.defaultTimeZone, Locale.US);
this.calendar = Calendar.getInstance(this.serverTimeZone, Locale.US);
}

this.calendar.setTime((java.util.Date) this.value);
Expand All @@ -272,14 +281,46 @@ private void storeTime(NativePacketPayload intoPacket) {
intoPacket.writeInteger(IntegerDataType.INT1, this.calendar.get(Calendar.SECOND));
}

private void storeDate(NativePacketPayload intoPacket) {
synchronized (this) {
if (this.calendar == null) {
this.calendar = Calendar.getInstance(this.cacheDefaultTimezone.getValue() ? this.defaultTimeZone : TimeZone.getDefault(), Locale.US);
}

this.calendar.setTime((java.util.Date) this.value);

this.calendar.set(Calendar.HOUR_OF_DAY, 0);
this.calendar.set(Calendar.MINUTE, 0);
this.calendar.set(Calendar.SECOND, 0);

byte length = (byte) 7;

intoPacket.ensureCapacity(length);

intoPacket.writeInteger(IntegerDataType.INT1, length); // length

int year = this.calendar.get(Calendar.YEAR);
int month = this.calendar.get(Calendar.MONTH) + 1;
int date = this.calendar.get(Calendar.DAY_OF_MONTH);

intoPacket.writeInteger(IntegerDataType.INT2, year);
intoPacket.writeInteger(IntegerDataType.INT1, month);
intoPacket.writeInteger(IntegerDataType.INT1, date);

intoPacket.writeInteger(IntegerDataType.INT1, 0);
intoPacket.writeInteger(IntegerDataType.INT1, 0);
intoPacket.writeInteger(IntegerDataType.INT1, 0);
}
}

/**
* @param intoPacket
* packet to write into
*/
private void storeDateTime(NativePacketPayload intoPacket) {
synchronized (this) {
if (this.calendar == null) {
this.calendar = Calendar.getInstance(this.defaultTimeZone, Locale.US);
this.calendar = Calendar.getInstance(this.serverTimeZone, Locale.US);
}

this.calendar.setTime((java.util.Date) this.value);
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
Expand Down Expand Up @@ -69,7 +69,8 @@ public ServerPreparedQueryBindings(int parameterCount, Session sess) {
protected void initBindValues(int parameterCount) {
this.bindValues = new ServerPreparedQueryBindValue[parameterCount];
for (int i = 0; i < parameterCount; i++) {
this.bindValues[i] = new ServerPreparedQueryBindValue(this.session.getServerSession().getDefaultTimeZone());
this.bindValues[i] = new ServerPreparedQueryBindValue(this.session.getServerSession().getDefaultTimeZone(),
this.session.getServerSession().getServerTimeZone(), this.session.getPropertySet());
}
}

Expand Down Expand Up @@ -469,7 +470,7 @@ public void setTime(int parameterIndex, Time x, Calendar cal) {
}

public void setTime(int parameterIndex, Time x) {
setTime(parameterIndex, x, null); // this.session.getServerSession().getDefaultTimeZone()
setTime(parameterIndex, x, null);
}

@Override
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
Expand Down Expand Up @@ -2134,7 +2134,6 @@ public void configureTimezone() {
}
}

this.serverSession.setDefaultTimeZone(this.serverSession.getServerTimeZone());
}

@Override
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
Expand Down Expand Up @@ -38,6 +38,7 @@
import com.mysql.cj.ServerVersion;
import com.mysql.cj.conf.PropertyKey;
import com.mysql.cj.conf.PropertySet;
import com.mysql.cj.conf.RuntimeProperty;
import com.mysql.cj.exceptions.ExceptionFactory;
import com.mysql.cj.exceptions.WrongArgumentException;
import com.mysql.cj.protocol.ServerCapabilities;
Expand Down Expand Up @@ -116,11 +117,13 @@ public class NativeServerSession implements ServerSession {
/** The timezone of the server */
private TimeZone serverTimeZone = null;

/** c.f. getDefaultTimeZone(). this value may be overridden during connection initialization */
private TimeZone defaultTimeZone = TimeZone.getDefault();

private RuntimeProperty<Boolean> cacheDefaultTimezone = null;

public NativeServerSession(PropertySet propertySet) {
this.propertySet = propertySet;
this.cacheDefaultTimezone = this.propertySet.getBooleanProperty(PropertyKey.cacheDefaultTimezone);

// preconfigure some server variables which are consulted before their initialization from server
this.serverVariables.put("character_set_server", "utf8");
Expand Down Expand Up @@ -546,10 +549,9 @@ public void setServerTimeZone(TimeZone serverTimeZone) {
}

public TimeZone getDefaultTimeZone() {
return this.defaultTimeZone;
}

public void setDefaultTimeZone(TimeZone defaultTimeZone) {
this.defaultTimeZone = defaultTimeZone;
if (this.cacheDefaultTimezone.getValue()) {
return this.defaultTimeZone;
}
return TimeZone.getDefault();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
Expand Down Expand Up @@ -47,7 +47,6 @@ public class XServerSession implements ServerSession {
/** The timezone of the server */
//private TimeZone serverTimeZone = null;

/** c.f. getDefaultTimeZone(). this value may be overridden during connection initialization */
private TimeZone defaultTimeZone = TimeZone.getDefault();

@Override
Expand Down Expand Up @@ -334,8 +333,4 @@ public void setServerTimeZone(TimeZone serverTimeZone) {
public TimeZone getDefaultTimeZone() {
return this.defaultTimeZone;
}

public void setDefaultTimeZone(TimeZone defaultTimeZone) {
this.defaultTimeZone = defaultTimeZone;
}
}
Expand Up @@ -813,6 +813,7 @@ ConnectionProperties.metadataCacheSize=The number of queries to cache ResultSetM
ConnectionProperties.netTimeoutForStreamingResults=What value should the driver automatically set the server setting ''net_write_timeout'' to when the streaming result sets feature is in use? (value has unit of seconds, the value ''0'' means the driver will not try and adjust this value)
ConnectionProperties.noAccessToProcedureBodies=When determining procedure parameter types for CallableStatements, and the connected user can''t access procedure bodies through "SHOW CREATE PROCEDURE" or select on mysql.proc should the driver instead create basic metadata (all parameters reported as INOUT VARCHARs) instead of throwing an exception?
ConnectionProperties.noDatetimeStringSync=Don''t ensure that ResultSet.getDatetimeType().toString().equals(ResultSet.getString())
ConnectionProperties.cacheDefaultTimezone=Caches client's default time zone. This results in better performance when dealing with time zone conversions in Date and Time data types, however it won't be aware of time zone changes if they happen at runtime.
ConnectionProperties.nullCatalogMeansCurrent=When DatabaseMetadata methods ask for a ''catalog'' or ''schema'' parameter, does the value null mean use the current database? See also property ''databaseTerm''.
ConnectionProperties.databaseTerm=MySQL uses the term "schema" as a synonym of the term "database," while Connector/J historically takes the JDBC term "catalog" as synonymous to "database". This property sets for Connector/J which of the JDBC terms "catalog" and "schema" is used in an application to refer to a database. The property takes one of the two values CATALOG or SCHEMA and uses it to determine (1) which Connection methods can be used to set/get the current database (e.g. setCatalog() or setSchema()?), (2) which arguments can be used within the various DatabaseMetaData methods to filter results (e.g. the catalog or schemaPattern argument of getColumns()?), and (3) which fields in the ResultSet returned by DatabaseMetaData methods contain the database identification information (i.e., the TABLE_CAT or TABLE_SCHEM field in the ResultSet returned by getTables()?).[CR]If databaseTerm=CATALOG, schemaPattern for searches are ignored and calls of schema methods (like setSchema() or get Schema()) become no-ops, and vice versa.
ConnectionProperties.packetDebugBufferSize=The maximum number of packets to retain when ''enablePacketDebug'' is true
Expand Down
4 changes: 2 additions & 2 deletions src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionImpl.java
Expand Up @@ -1478,7 +1478,7 @@ public String nativeSQL(String sql) throws SQLException {
return null;
}

Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, getMultiHostSafeProxy().getSession().getServerSession().getDefaultTimeZone(),
Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, getMultiHostSafeProxy().getSession().getServerSession().getServerTimeZone(),
getMultiHostSafeProxy().getSession().getServerSession().getCapabilities().serverSupportsFracSecs(),
getMultiHostSafeProxy().getSession().getServerSession().isServerTruncatesFracSecs(), getExceptionInterceptor());

Expand All @@ -1490,7 +1490,7 @@ public String nativeSQL(String sql) throws SQLException {
}

private CallableStatement parseCallableStatement(String sql) throws SQLException {
Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, getMultiHostSafeProxy().getSession().getServerSession().getDefaultTimeZone(),
Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, getMultiHostSafeProxy().getSession().getServerSession().getServerTimeZone(),
getMultiHostSafeProxy().getSession().getServerSession().getCapabilities().serverSupportsFracSecs(),
getMultiHostSafeProxy().getSession().getServerSession().isServerTruncatesFracSecs(), getExceptionInterceptor());

Expand Down
12 changes: 6 additions & 6 deletions src/main/user-impl/java/com/mysql/cj/jdbc/EscapeProcessor.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
Expand Down Expand Up @@ -84,8 +84,8 @@ class EscapeProcessor {
*
* @param sql
* the SQL to escape process.
* @param defaultTimeZone
* time zone
* @param serverTimeZone
* server time zone
* @param serverSupportsFractionalSecond
* flag indicating if server supports fractional seconds
* @param serverTruncatesFractionalSecond
Expand All @@ -98,7 +98,7 @@ class EscapeProcessor {
* @throws SQLException
* if error occurs
*/
public static final Object escapeSQL(String sql, TimeZone defaultTimeZone, boolean serverSupportsFractionalSecond, boolean serverTruncatesFractionalSecond,
public static final Object escapeSQL(String sql, TimeZone serverTimeZone, boolean serverSupportsFractionalSecond, boolean serverTruncatesFractionalSecond,
ExceptionInterceptor exceptionInterceptor) throws java.sql.SQLException {
boolean replaceEscapeSequence = false;
String escapeSequence = null;
Expand Down Expand Up @@ -140,7 +140,7 @@ public static final Object escapeSQL(String sql, TimeZone defaultTimeZone, boole
if (nestedBrace != -1) {
StringBuilder buf = new StringBuilder(token.substring(0, 1));

Object remainingResults = escapeSQL(token.substring(1, token.length() - 1), defaultTimeZone, serverSupportsFractionalSecond,
Object remainingResults = escapeSQL(token.substring(1, token.length() - 1), serverTimeZone, serverSupportsFractionalSecond,
serverTruncatesFractionalSecond, exceptionInterceptor);

String remaining = null;
Expand Down Expand Up @@ -223,7 +223,7 @@ public static final Object escapeSQL(String sql, TimeZone defaultTimeZone, boole
}
}
} else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{ts")) {
processTimestampToken(defaultTimeZone, newSql, token, serverSupportsFractionalSecond, serverTruncatesFractionalSecond,
processTimestampToken(serverTimeZone, newSql, token, serverSupportsFractionalSecond, serverTruncatesFractionalSecond,
exceptionInterceptor);
} else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{t")) {
processTimeToken(newSql, token, serverSupportsFractionalSecond, exceptionInterceptor);
Expand Down

0 comments on commit c1e9c7d

Please sign in to comment.