oceanbase / obconnector-j

OceanBase Client for Java is a JDBC 4.2 compatible driver, used to connect applications developed in Java to OceanBase Database Server.
GNU Lesser General Public License v2.1
28 stars 10 forks source link

[Bug]: failed to connect to server when client timezone is UTC #8

Open ldming opened 4 months ago

ldming commented 4 months ago

Describe the bug

When I run a java code to connect to Oceanbase and I set the default time zone to UTC or GMT+00:00, an exception will be throwed:

Failed to establish a connection: Could not connect to *****:2881 : (conn=3221584558) The server time_zone 'GMT+08:00' defined in the 'serverTimezone' parameter cannot be parsed by java TimeZone implementation. See java.util.TimeZone#getAvailableIDs() for available TimeZone, depending on your JRE implementation.
java.sql.SQLTransientConnectionException: Could not connect to ******:2881 : (conn=3221584558) The server time_zone 'GMT+08:00' defined in the 'serverTimezone' parameter cannot be parsed by java TimeZone implementation. See java.util.TimeZone#getAvailableIDs() for available TimeZone, depending on your JRE implementation.
        at com.oceanbase.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:128)
        at com.oceanbase.jdbc.internal.util.exceptions.ExceptionFactory.create(ExceptionFactory.java:235)
        at com.oceanbase.jdbc.internal.protocol.AbstractConnectProtocol.connectWithoutProxy(AbstractConnectProtocol.java:1644)
        at com.oceanbase.jdbc.internal.util.Utils.retrieveProxy(Utils.java:1427)
        at com.oceanbase.jdbc.OceanBaseConnection.newConnection(OceanBaseConnection.java:306)
        at com.oceanbase.jdbc.Driver.connect(Driver.java:89)
        at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:681)
        at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:229)
        at com.example.apecloud.DatabaseManager.getConnection(DatabaseManager.java:67)
        at com.example.apecloud.DatabaseManager.testConnection(DatabaseManager.java:39)
        at com.example.Main.getConn(Main.java:159)
        at com.example.Main.main(Main.java:42)

Environment

Mac and Java.

Fast reproduce steps

String tz = "GMT" + "+00:00";
TimeZone timeZone = Utils.getTimeZone(tz);
TimeZone.setDefault(timeZone);
String host=1.2.3.4
String jdbcUrl = "jdbc:oceanbase://" + host + ":2881/TEST?serverTimezone=Asia/Shanghai";
String username = "TEST@test";
String password = "123455";
return DriverManager.getConnection(jdbcUrl, username, password);

Expected behavior

Connect to the Oceanbase successfully.

Actual behavior

Throw exception.

Additional context

I analyzed the source code, and I believe the issue lies within the sendAlterSessionTimezone function.

    private void sendAlterSessionTimezone() throws IOException, SQLException {
        this.importedTimeZoneTables = getServerTZTablesImported();
        String zoneId = this.importedTimeZoneTables ? TimeZone.getDefault().getID()
            : TimeZone.getDefault().toZoneId().getRules().getStandardOffset(Instant.now()).getId();
        // when importedTimeZoneTables is
        //   false: the TIME_ZONE cannot be set to any values that start with "GMT"
        //   true: only GMT, GMT+0, GMT-0, GMT0 can be accepted
        if (zoneId.startsWith("GMT") && (!this.importedTimeZoneTables
                || !zoneId.equalsIgnoreCase("GMT")|| !zoneId.equalsIgnoreCase("GMT+0")
                || !zoneId.equalsIgnoreCase("GMT-0") || !zoneId.equalsIgnoreCase("GMT0"))) {
            zoneId = zoneId.substring("GMT".length());
        }

        writer.startPacket(0);
        writer.write(Packet.COM_QUERY);
        writer.write(ALTER_SESSION_TIMEZONE_QUERY);
        writer.write(zoneId.getBytes());
        writer.write('\'');
        writer.flush();
    }

When the default timezone is UTC, the TimeZone.getDefault().toZoneId().getRules().getStandardOffset(Instant.now()).getId() will return Z, and Z is not a valid timezone for alter session set TIME_ZONE that will throw an exception, unfortunately, this exception message is not been printed, so it is difficult to clearly see where the problem lies.

IMO, this function should check if the zoneId is Z, if yes, convert it to a valid timezone format.