ClickHouse / clickhouse-java

ClickHouse Java Clients & JDBC Driver
https://clickhouse.com
Apache License 2.0
1.45k stars 535 forks source link

setTimestamp java.lang.NullPointerException #293

Open kuangye098 opened 5 years ago

kuangye098 commented 5 years ago

JDBC version : 0.1.48 and JDK 1.8.171

      ClickHouseProperties properties = new ClickHouseProperties();
      properties.setUseServerTimeZone(false);
      ClickHouseProperties withCredentials = properties.withCredentials(this.username, this.password);
      this.clickHouseDataSource = new BalancedClickhouseDataSource(this.jdbcUrl, withCredentials);
  ```````     
  ```````
  java.sql.Timestamp sqlTimestamp =  new java.sql.Timestamp(
                            1548042771L);
 preparedStatement.setTimestamp(columnIndex + 1, sqlTimestamp);

Caused by: java.lang.NullPointerException
        at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2342)
        at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2312)
        at java.util.Calendar.setTimeInMillis(Calendar.java:1804)
        at java.util.Calendar.setTime(Calendar.java:1770)
        at java.text.SimpleDateFormat.format(SimpleDateFormat.java:943)
        at java.text.SimpleDateFormat.format(SimpleDateFormat.java:936)
        at java.text.DateFormat.format(DateFormat.java:345)
        at ru.yandex.clickhouse.ClickHousePreparedStatementImpl.setTimestamp(ClickHousePreparedStatementImpl.java:247)
        at com.xx.datax.plugin.writer.clickhousewriter.CHWriter$Task.fillPreparedStatementColumnType(CHWriter.java:361)
        at com.xx.datax.plugin.writer.clickhousewriter.CHWriter$Task.fillPreparedStatement(CHWriter.java:272)
        at com.xx.datax.plugin.writer.clickhousewriter.CHWriter$Task.doBatchInsert(CHWriter.java:250)
        ... 5 more
kuangye098 commented 5 years ago

It took me 2 hours of debugging and found that the time zone setting of the ClickHouse client was incorrect. The following code has logical processing that is not rigorous enough. I think it should be handled this way.

ClickHouseConnectionImpl.java

private void initTimeZone(ClickHouseProperties properties) {
        if (properties.isUseServerTimeZone() && !Strings.isNullOrEmpty(properties.getUseTimeZone())) {
            throw new IllegalArgumentException(String.format("only one of %s or %s must be enabled", ClickHouseConnectionSettings.USE_SERVER_TIME_ZONE.getKey(), ClickHouseConnectionSettings.USE_TIME_ZONE.getKey()));
        }
        if (properties.isUseServerTimeZone()) {
            ResultSet rs = null;
            try {
                timezone = TimeZone.getTimeZone("UTC"); // just for next query
                rs = createStatement().executeQuery("select timezone()");
                rs.next();
                String timeZoneName = rs.getString(1);
                timezone = TimeZone.getTimeZone(timeZoneName);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                StreamUtils.close(rs);
            }
        } else if (!Strings.isNullOrEmpty(properties.getUseTimeZone())) {
            timezone = TimeZone.getTimeZone(properties.getUseTimeZone());
        }
    }
  1. Use the server time zone as the local ClickHouse client time zone.
  2. The time zone set by the developer itself is the ClickHouse client time zone.

Obviously, the first IF statement in this code does not judge that their values are False, and then does not check the TimeZone value, causing the above exception to occur. But I think the JDK is also responsible, not doing a non-empty check :).