From 94821de187b6e755cf17ce5ea9e7d5e9f40579ba Mon Sep 17 00:00:00 2001 From: Paultagoras Date: Wed, 22 Jan 2025 10:40:50 -0500 Subject: [PATCH 1/6] Tweaking timezones --- .../jdbc/PreparedStatementImpl.java | 6 +-- .../com/clickhouse/jdbc/ResultSetImpl.java | 45 +++++++++---------- .../com/clickhouse/jdbc/DataTypeTests.java | 10 ++--- .../jdbc/PreparedStatementTest.java | 4 +- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java index 9757503c7..1ab25363a 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java @@ -269,7 +269,7 @@ public ResultSetMetaData getMetaData() throws SQLException { public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { checkClosed(); if (cal == null) { - cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));//This says whatever date is in UTC + cal = new GregorianCalendar();//This says whatever date is in UTC } LocalDate d = x.toLocalDate(); @@ -283,7 +283,7 @@ public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLExceptio public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { checkClosed(); if (cal == null) { - cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + cal = new GregorianCalendar(); } LocalTime t = x.toLocalTime(); @@ -297,7 +297,7 @@ public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLExceptio public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { checkClosed(); if (cal == null) { - cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + cal = new GregorianCalendar(); } LocalDateTime ldt = x.toLocalDateTime(); diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java index 470fc0ef2..9b6149862 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -38,7 +38,6 @@ public class ResultSetImpl implements ResultSet, JdbcV2Wrapper { private boolean closed; private final StatementImpl parentStatement; private boolean wasNull; - private final Calendar defaultCalendar; public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, ClickHouseBinaryFormatReader reader) { this.parentStatement = parentStatement; @@ -47,7 +46,6 @@ public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, Clic this.metaData = new com.clickhouse.jdbc.metadata.ResultSetMetaData(this); this.closed = false; this.wasNull = false; - this.defaultCalendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); } protected ResultSetImpl(ResultSetImpl resultSet) { @@ -57,7 +55,6 @@ protected ResultSetImpl(ResultSetImpl resultSet) { this.metaData = resultSet.metaData; this.closed = false; this.wasNull = false; - this.defaultCalendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); } private void checkClosed() throws SQLException { @@ -1048,17 +1045,18 @@ public Date getDate(int columnIndex, Calendar cal) throws SQLException { public Date getDate(String columnLabel, Calendar cal) throws SQLException { checkClosed(); try { - LocalDate d = reader.getLocalDate(columnLabel); - if (d == null) { + ZonedDateTime zdt = reader.getZonedDateTime(columnLabel); + if (zdt == null) { wasNull = true; return null; } wasNull = false; - Calendar c = (Calendar) (cal != null ? cal : defaultCalendar).clone(); - c.clear(); - c.set(d.getYear(), d.getMonthValue() - 1, d.getDayOfMonth(), 0, 0, 0); - return new Date(c.getTimeInMillis()); + if (cal == null) { + cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + } + + return new Date(zdt.withZoneSameLocal(cal.getTimeZone().toZoneId()).toInstant().toEpochMilli());//This assumes the response is in cal.getTimeZone() } catch (Exception e) { throw ExceptionUtils.toSqlState(String.format("Method: getDate(\"%s\") encountered an exception.", columnLabel), String.format("SQL: [%s]", parentStatement.getLastSql()), e); } @@ -1080,10 +1078,11 @@ public Time getTime(String columnLabel, Calendar cal) throws SQLException { } wasNull = false; - Calendar c = (Calendar)( cal != null ? cal : defaultCalendar).clone(); - c.clear(); - c.set(1970, Calendar.JANUARY, 1, zdt.getHour(), zdt.getMinute(), zdt.getSecond()); - return new Time(c.getTimeInMillis()); + if (cal == null) { + cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + } + + return new Time(zdt.withZoneSameLocal(cal.getTimeZone().toZoneId()).toInstant().toEpochMilli());//This assumes the response is in cal.getTimeZone() } catch (Exception e) { throw ExceptionUtils.toSqlState(String.format("Method: getTime(\"%s\") encountered an exception.", columnLabel), String.format("SQL: [%s]", parentStatement.getLastSql()), e); } @@ -1098,23 +1097,23 @@ public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { checkClosed(); try { - LocalDateTime localDateTime = reader.getLocalDateTime(columnLabel); - if (localDateTime == null) { + ZonedDateTime zdt = reader.getZonedDateTime(columnLabel); + if (zdt == null) { wasNull = true; return null; } wasNull = false; - - Calendar c = (Calendar) (cal != null ? cal : defaultCalendar).clone(); - c.set(localDateTime.getYear(), localDateTime.getMonthValue() - 1, localDateTime.getDayOfMonth(), localDateTime.getHour(), localDateTime.getMinute(), - localDateTime.getSecond()); - Timestamp timestamp = new Timestamp(c.getTimeInMillis()); - timestamp.setNanos(localDateTime.getNano()); - return timestamp; + + if (cal == null) { + cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + } + + Timestamp ts = new Timestamp(zdt.withZoneSameLocal(cal.getTimeZone().toZoneId()).toInstant().toEpochMilli()); + ts.setNanos(zdt.getNano()); + return ts;//This assumes the response is in cal.getTimeZone() } catch (Exception e) { throw ExceptionUtils.toSqlState(String.format("Method: getTimestamp(\"%s\") encountered an exception.", columnLabel), String.format("SQL: [%s]", parentStatement.getLastSql()), e); } - } @Override diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java index cb85158b1..a8ccfca59 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java @@ -301,11 +301,11 @@ public void testDateTypes() throws SQLException { assertTrue(rs.next()); assertEquals(rs.getDate("date", new GregorianCalendar()).toString(), date.toString()); assertEquals(rs.getDate("date32", new GregorianCalendar()).toString(), date32.toString()); - assertEquals(rs.getTimestamp("dateTime", new GregorianCalendar()).toString(), dateTime.toString()); - assertEquals(rs.getTimestamp("dateTime32", new GregorianCalendar()).toString(), dateTime32.toString()); - assertEquals(rs.getTimestamp("dateTime643", new GregorianCalendar()).toString(), dateTime643.toString()); - assertEquals(rs.getTimestamp("dateTime646", new GregorianCalendar()).toString(), dateTime646.toString()); - assertEquals(rs.getTimestamp("dateTime649", new GregorianCalendar()).toString(), dateTime649.toString()); + assertEquals(rs.getTimestamp("dateTime").toString(), dateTime.toString()); + assertEquals(rs.getTimestamp("dateTime32").toString(), dateTime32.toString()); + assertEquals(rs.getTimestamp("dateTime643").toString(), dateTime643.toString()); + assertEquals(rs.getTimestamp("dateTime646").toString(), dateTime646.toString()); + assertEquals(rs.getTimestamp("dateTime649").toString(), dateTime649.toString()); assertFalse(rs.next()); } diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java index 9de9a6a58..988b78746 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java @@ -186,7 +186,7 @@ public void testSetTime() throws Exception { stmt.setTime(1, java.sql.Time.valueOf("12:34:56")); try (ResultSet rs = stmt.executeQuery()) { assertTrue(rs.next()); - assertEquals(rs.getTime(1, new GregorianCalendar()).toString(), "12:34:56"); + assertEquals(rs.getTime(1).toString(), "12:34:56"); assertFalse(rs.next()); } } @@ -201,7 +201,7 @@ public void testSetTimestamp() throws Exception { stmt.setTimestamp(2, java.sql.Timestamp.valueOf("2021-01-01 01:34:56.456"), java.util.Calendar.getInstance(java.util.TimeZone.getTimeZone("UTC"))); try (ResultSet rs = stmt.executeQuery()) { assertTrue(rs.next()); - assertEquals(rs.getTimestamp(1, new GregorianCalendar()).toString(), "2021-01-01 01:34:56.456"); + assertEquals(rs.getTimestamp(1).toString(), "2021-01-01 01:34:56.456"); assertEquals(rs.getTimestamp(2, new GregorianCalendar()).toString(), "2021-01-01 01:34:56.456"); assertFalse(rs.next()); } From e46aac9a23bade07514f9fabc495d4becbba833e Mon Sep 17 00:00:00 2001 From: Paultagoras Date: Wed, 22 Jan 2025 11:03:37 -0500 Subject: [PATCH 2/6] Tweaking handling --- .../com/clickhouse/jdbc/ResultSetImpl.java | 12 ++++++------ .../com/clickhouse/jdbc/DataTypeTests.java | 14 +++++++------- .../jdbc/PreparedStatementTest.java | 5 +++-- .../com/clickhouse/jdbc/StatementTest.java | 19 ++++++++++--------- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java index 9b6149862..fc401b14a 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -1053,10 +1053,10 @@ public Date getDate(String columnLabel, Calendar cal) throws SQLException { wasNull = false; if (cal == null) { - cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + cal = new GregorianCalendar(); } - return new Date(zdt.withZoneSameLocal(cal.getTimeZone().toZoneId()).toInstant().toEpochMilli());//This assumes the response is in cal.getTimeZone() + return Date.valueOf(zdt.withZoneSameInstant(cal.getTimeZone().toZoneId()).toLocalDate()); } catch (Exception e) { throw ExceptionUtils.toSqlState(String.format("Method: getDate(\"%s\") encountered an exception.", columnLabel), String.format("SQL: [%s]", parentStatement.getLastSql()), e); } @@ -1079,10 +1079,10 @@ public Time getTime(String columnLabel, Calendar cal) throws SQLException { wasNull = false; if (cal == null) { - cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + cal = new GregorianCalendar(); } - return new Time(zdt.withZoneSameLocal(cal.getTimeZone().toZoneId()).toInstant().toEpochMilli());//This assumes the response is in cal.getTimeZone() + return Time.valueOf(zdt.withZoneSameInstant(cal.getTimeZone().toZoneId()).toLocalTime()); } catch (Exception e) { throw ExceptionUtils.toSqlState(String.format("Method: getTime(\"%s\") encountered an exception.", columnLabel), String.format("SQL: [%s]", parentStatement.getLastSql()), e); } @@ -1105,10 +1105,10 @@ public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLExcept wasNull = false; if (cal == null) { - cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + cal = new GregorianCalendar(); } - Timestamp ts = new Timestamp(zdt.withZoneSameLocal(cal.getTimeZone().toZoneId()).toInstant().toEpochMilli()); + Timestamp ts = Timestamp.valueOf(zdt.withZoneSameInstant(cal.getTimeZone().toZoneId()).toLocalDateTime()); ts.setNanos(zdt.getNano()); return ts;//This assumes the response is in cal.getTimeZone() } catch (Exception e) { diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java index a8ccfca59..bf7240467 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java @@ -281,8 +281,8 @@ public void testDateTypes() throws SQLException { try (Statement stmt = conn.createStatement()) { try (ResultSet rs = stmt.executeQuery("SELECT * FROM test_dates ORDER BY order")) { assertTrue(rs.next()); - assertEquals(rs.getDate("date", new GregorianCalendar()), Date.valueOf("1970-01-01")); - assertEquals(rs.getDate("date32", new GregorianCalendar()), Date.valueOf("1970-01-01")); + assertEquals(rs.getDate("date", new GregorianCalendar(TimeZone.getTimeZone("UTC"))), Date.valueOf("1970-01-01")); + assertEquals(rs.getDate("date32", new GregorianCalendar(TimeZone.getTimeZone("UTC"))), Date.valueOf("1970-01-01")); assertEquals(rs.getTimestamp("dateTime").toInstant().toString(), "1970-01-01T00:00:00Z"); assertEquals(rs.getTimestamp("dateTime32").toInstant().toString(), "1970-01-01T00:00:00Z"); assertEquals(rs.getTimestamp("dateTime643").toInstant().toString(), "1970-01-01T00:00:00Z"); @@ -290,8 +290,8 @@ public void testDateTypes() throws SQLException { assertEquals(rs.getTimestamp("dateTime649").toInstant().toString(), "1970-01-01T00:00:00Z"); assertTrue(rs.next()); - assertEquals(rs.getDate("date", new GregorianCalendar()), Date.valueOf("2149-06-06")); - assertEquals(rs.getDate("date32", new GregorianCalendar()), Date.valueOf("2299-12-31")); + assertEquals(rs.getDate("date", new GregorianCalendar(TimeZone.getTimeZone("UTC"))), Date.valueOf("2149-06-06")); + assertEquals(rs.getDate("date32", new GregorianCalendar(TimeZone.getTimeZone("UTC"))), Date.valueOf("2299-12-31")); assertEquals(rs.getTimestamp("dateTime").toInstant().toString(), "2106-02-07T06:28:15Z"); assertEquals(rs.getTimestamp("dateTime32").toInstant().toString(), "2106-02-07T06:28:15Z"); assertEquals(rs.getTimestamp("dateTime643").toInstant().toString(), "2261-12-31T23:59:59.999Z"); @@ -299,8 +299,8 @@ public void testDateTypes() throws SQLException { assertEquals(rs.getTimestamp("dateTime649").toInstant().toString(), "2261-12-31T23:59:59.999999999Z"); assertTrue(rs.next()); - assertEquals(rs.getDate("date", new GregorianCalendar()).toString(), date.toString()); - assertEquals(rs.getDate("date32", new GregorianCalendar()).toString(), date32.toString()); + assertEquals(rs.getDate("date", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), date.toString()); + assertEquals(rs.getDate("date32", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), date32.toString()); assertEquals(rs.getTimestamp("dateTime").toString(), dateTime.toString()); assertEquals(rs.getTimestamp("dateTime32").toString(), dateTime32.toString()); assertEquals(rs.getTimestamp("dateTime643").toString(), dateTime643.toString()); @@ -887,7 +887,7 @@ public void testTypeConversions() throws Exception { assertEquals(rs.getObject(3, Double.class), 1.0); assertEquals(String.valueOf(rs.getObject(3, new HashMap>(){{put(JDBCType.FLOAT.getName(), Float.class);}})), "1.0"); - assertEquals(rs.getDate(4, new GregorianCalendar()), Date.valueOf("2024-12-01")); + assertEquals(rs.getDate(4, new GregorianCalendar(TimeZone.getTimeZone("UTC"))), Date.valueOf("2024-12-01")); assertTrue(rs.getObject(4) instanceof Date); assertEquals(rs.getObject(4), Date.valueOf("2024-12-01")); assertEquals(rs.getString(4), "2024-12-01");//Underlying object is ZonedDateTime diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java index 988b78746..0fb953eb8 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java @@ -14,6 +14,7 @@ import java.util.Arrays; import java.util.Calendar; import java.util.GregorianCalendar; +import java.util.TimeZone; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; @@ -172,7 +173,7 @@ public void testSetDate() throws Exception { stmt.setDate(1, java.sql.Date.valueOf("2021-01-01")); try (ResultSet rs = stmt.executeQuery()) { assertTrue(rs.next()); - assertEquals(rs.getDate(1, new GregorianCalendar()), java.sql.Date.valueOf("2021-01-01")); + assertEquals(rs.getDate(1, new GregorianCalendar(TimeZone.getTimeZone("UTC"))), java.sql.Date.valueOf("2021-01-01")); assertFalse(rs.next()); } } @@ -202,7 +203,7 @@ public void testSetTimestamp() throws Exception { try (ResultSet rs = stmt.executeQuery()) { assertTrue(rs.next()); assertEquals(rs.getTimestamp(1).toString(), "2021-01-01 01:34:56.456"); - assertEquals(rs.getTimestamp(2, new GregorianCalendar()).toString(), "2021-01-01 01:34:56.456"); + assertEquals(rs.getTimestamp(2, java.util.Calendar.getInstance(java.util.TimeZone.getTimeZone("UTC"))).toString(), "2021-01-01 01:34:56.456"); assertFalse(rs.next()); } } diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java index 5512da0d3..1ca7db374 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java @@ -24,6 +24,7 @@ import java.util.GregorianCalendar; import java.util.List; import java.util.Properties; +import java.util.TimeZone; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; @@ -126,12 +127,12 @@ public void testExecuteQueryDates() throws Exception { try (Statement stmt = conn.createStatement()) { try (ResultSet rs = stmt.executeQuery("SELECT toDate('2020-01-01') AS date, toDateTime('2020-01-01 10:11:12', 'Asia/Istanbul') AS datetime")) { assertTrue(rs.next()); - assertEquals(rs.getDate(1, new GregorianCalendar()).toString(), Date.valueOf("2020-01-01").toString()); - assertEquals(rs.getDate("date", new GregorianCalendar()).toString(), Date.valueOf("2020-01-01").toString()); + assertEquals(rs.getDate(1, new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), Date.valueOf("2020-01-01").toString()); + assertEquals(rs.getDate("date", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), Date.valueOf("2020-01-01").toString()); assertEquals(rs.getString(1), "2020-01-01"); assertEquals(rs.getString("date"), "2020-01-01"); - assertEquals(rs.getDate(2, new GregorianCalendar()).toString(), "2020-01-01"); - assertEquals(rs.getDate("datetime", new GregorianCalendar()).toString(), "2020-01-01"); + assertEquals(rs.getDate(2, new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), "2020-01-01"); + assertEquals(rs.getDate("datetime", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), "2020-01-01"); assertEquals(rs.getString(2), "2020-01-01T10:11:12+03:00[Asia/Istanbul]"); assertEquals(rs.getString("datetime"), "2020-01-01T10:11:12+03:00[Asia/Istanbul]"); assertFalse(rs.next()); @@ -243,13 +244,13 @@ public void testExecuteUpdateDates() throws Exception { assertEquals(stmt.executeUpdate("INSERT INTO " + getDatabase() + ".dates VALUES (0, '2020-01-01', '2020-01-01 10:11:12'), (1, NULL, '2020-01-01 12:10:07'), (2, '2020-01-01', NULL)"), 3); try (ResultSet rs = stmt.executeQuery("SELECT date, datetime FROM " + getDatabase() + ".dates ORDER BY id")) { assertTrue(rs.next()); - assertEquals(rs.getDate(1, new GregorianCalendar()).toString(), "2020-01-01"); - assertEquals(rs.getDate(2, new GregorianCalendar()).toString(), "2020-01-01"); + assertEquals(rs.getDate(1, new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), "2020-01-01"); + assertEquals(rs.getDate(2, new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), "2020-01-01"); assertTrue(rs.next()); assertNull(rs.getDate(1)); - assertEquals(rs.getDate(2, new GregorianCalendar()).toString(), "2020-01-01"); + assertEquals(rs.getDate(2).toString(), "2020-01-01"); assertTrue(rs.next()); - assertEquals(rs.getDate(1, new GregorianCalendar()).toString(), "2020-01-01"); + assertEquals(rs.getDate(1, new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), "2020-01-01"); assertNull(rs.getDate(2)); assertFalse(rs.next()); } @@ -299,7 +300,7 @@ public void testJdbcEscapeSyntax() throws Exception { "toInt32({fn LENGTH('Hello')}) AS FNLENGTH, toInt32({fn POSITION('Hello', 'l')}) AS FNPOSITION, toInt32({fn MOD(10, 3)}) AS FNMOD, " + "{fn SQRT(9)} AS FNSQRT, {fn SUBSTRING('Hello', 3, 2)} AS FNSUBSTRING")) { assertTrue(rs.next()); - assertEquals(rs.getDate(1, new GregorianCalendar()), Date.valueOf(LocalDate.of(2021, 11, 1))); + assertEquals(rs.getDate(1, new GregorianCalendar(TimeZone.getTimeZone("UTC"))), Date.valueOf(LocalDate.of(2021, 11, 1))); //assertEquals(rs.getTimestamp(2), java.sql.Timestamp.valueOf(LocalDateTime.of(2021, 11, 1, 12, 34, 56))); assertEquals(rs.getInt(3), 1); assertEquals(rs.getInt("FNABS"), 1); From 01a06c3528a038ac05f831529d118d383a4fdf12 Mon Sep 17 00:00:00 2001 From: Paultagoras Date: Wed, 22 Jan 2025 13:02:48 -0500 Subject: [PATCH 3/6] Tweaking date handling --- .../com/clickhouse/jdbc/PreparedStatementImpl.java | 2 +- .../main/java/com/clickhouse/jdbc/ResultSetImpl.java | 2 +- .../test/java/com/clickhouse/jdbc/DataTypeTests.java | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java index 1ab25363a..8dbc31f79 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java @@ -269,7 +269,7 @@ public ResultSetMetaData getMetaData() throws SQLException { public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { checkClosed(); if (cal == null) { - cal = new GregorianCalendar();//This says whatever date is in UTC + cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));//This says whatever date is in UTC } LocalDate d = x.toLocalDate(); diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java index fc401b14a..73f7964a4 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -1053,7 +1053,7 @@ public Date getDate(String columnLabel, Calendar cal) throws SQLException { wasNull = false; if (cal == null) { - cal = new GregorianCalendar(); + cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));//Default to UTC } return Date.valueOf(zdt.withZoneSameInstant(cal.getTimeZone().toZoneId()).toLocalDate()); diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java index bf7240467..1e27f19b6 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java @@ -281,8 +281,8 @@ public void testDateTypes() throws SQLException { try (Statement stmt = conn.createStatement()) { try (ResultSet rs = stmt.executeQuery("SELECT * FROM test_dates ORDER BY order")) { assertTrue(rs.next()); - assertEquals(rs.getDate("date", new GregorianCalendar(TimeZone.getTimeZone("UTC"))), Date.valueOf("1970-01-01")); - assertEquals(rs.getDate("date32", new GregorianCalendar(TimeZone.getTimeZone("UTC"))), Date.valueOf("1970-01-01")); + assertEquals(rs.getDate("date"), Date.valueOf("1970-01-01")); + assertEquals(rs.getDate("date32"), Date.valueOf("1970-01-01")); assertEquals(rs.getTimestamp("dateTime").toInstant().toString(), "1970-01-01T00:00:00Z"); assertEquals(rs.getTimestamp("dateTime32").toInstant().toString(), "1970-01-01T00:00:00Z"); assertEquals(rs.getTimestamp("dateTime643").toInstant().toString(), "1970-01-01T00:00:00Z"); @@ -290,8 +290,8 @@ public void testDateTypes() throws SQLException { assertEquals(rs.getTimestamp("dateTime649").toInstant().toString(), "1970-01-01T00:00:00Z"); assertTrue(rs.next()); - assertEquals(rs.getDate("date", new GregorianCalendar(TimeZone.getTimeZone("UTC"))), Date.valueOf("2149-06-06")); - assertEquals(rs.getDate("date32", new GregorianCalendar(TimeZone.getTimeZone("UTC"))), Date.valueOf("2299-12-31")); + assertEquals(rs.getDate("date"), Date.valueOf("2149-06-06")); + assertEquals(rs.getDate("date32"), Date.valueOf("2299-12-31")); assertEquals(rs.getTimestamp("dateTime").toInstant().toString(), "2106-02-07T06:28:15Z"); assertEquals(rs.getTimestamp("dateTime32").toInstant().toString(), "2106-02-07T06:28:15Z"); assertEquals(rs.getTimestamp("dateTime643").toInstant().toString(), "2261-12-31T23:59:59.999Z"); @@ -299,8 +299,8 @@ public void testDateTypes() throws SQLException { assertEquals(rs.getTimestamp("dateTime649").toInstant().toString(), "2261-12-31T23:59:59.999999999Z"); assertTrue(rs.next()); - assertEquals(rs.getDate("date", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), date.toString()); - assertEquals(rs.getDate("date32", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), date32.toString()); + assertEquals(rs.getDate("date").toString(), date.toString()); + assertEquals(rs.getDate("date32").toString(), date32.toString()); assertEquals(rs.getTimestamp("dateTime").toString(), dateTime.toString()); assertEquals(rs.getTimestamp("dateTime32").toString(), dateTime32.toString()); assertEquals(rs.getTimestamp("dateTime643").toString(), dateTime643.toString()); From d8ef3041125864328c0a75bbfe392429f75832c4 Mon Sep 17 00:00:00 2001 From: Paultagoras Date: Wed, 22 Jan 2025 15:00:19 -0500 Subject: [PATCH 4/6] Changing the default calendar back and adding a comparison test --- .../comparison/DateTimeComparisonTest.java | 163 ++++++++++++++++++ .../com/clickhouse/jdbc/ResultSetImpl.java | 34 ++-- 2 files changed, 180 insertions(+), 17 deletions(-) create mode 100644 clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/comparison/DateTimeComparisonTest.java diff --git a/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/comparison/DateTimeComparisonTest.java b/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/comparison/DateTimeComparisonTest.java new file mode 100644 index 000000000..44d928e7a --- /dev/null +++ b/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/comparison/DateTimeComparisonTest.java @@ -0,0 +1,163 @@ +package com.clickhouse.jdbc.comparison; + +import com.clickhouse.client.ClickHouseProtocol; +import com.clickhouse.client.ClickHouseServerForTest; +import com.clickhouse.client.api.ClientConfigProperties; +import com.clickhouse.jdbc.ConnectionImpl; +import com.clickhouse.jdbc.JdbcIntegrationTest; +import com.clickhouse.jdbc.internal.ClickHouseConnectionImpl; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.GregorianCalendar; +import java.util.Properties; +import java.util.TimeZone; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertThrows; +import static org.testng.Assert.assertTrue; + +public class DateTimeComparisonTest extends JdbcIntegrationTest { + public Connection getJdbcConnectionV1(Properties properties) throws SQLException { + Properties info = new Properties(); + info.setProperty("user", "default"); + info.setProperty("password", ClickHouseServerForTest.getPassword()); + info.setProperty("database", ClickHouseServerForTest.getDatabase()); + + if (properties != null) { + info.putAll(properties); + } + + return new ClickHouseConnectionImpl(getJDBCEndpointString(), info); + } + + public Connection getJdbcConnectionV2(Properties properties) throws SQLException { + Properties info = new Properties(); + info.setProperty("user", "default"); + info.setProperty("password", ClickHouseServerForTest.getPassword()); + info.setProperty("database", ClickHouseServerForTest.getDatabase()); + + if (properties != null) { + info.putAll(properties); + } + + info.setProperty(ClientConfigProperties.DATABASE.getKey(), ClickHouseServerForTest.getDatabase()); + + return new ConnectionImpl(getJDBCEndpointString(), info); + } + + public String getJDBCEndpointString() { + return "jdbc:ch:" + (isCloud() ? "" : "http://") + + ClickHouseServerForTest.getClickHouseAddress(ClickHouseProtocol.HTTP, false) + "/" + (isCloud() ? ClickHouseServerForTest.getDatabase() : ""); + } + + private void run(String query) throws SQLException { + try (Connection connection = getJdbcConnectionV2(null)) { + try (Statement stmt = connection.createStatement()) { + stmt.execute("CREATE DATABASE IF NOT EXISTS " + ClickHouseServerForTest.getDatabase()); + stmt.execute(query); + } + } + } + + @Test (groups = "integration", enabled = true) + public void setDateTest() throws SQLException { + run("DROP TABLE IF EXISTS test_date"); + run("CREATE TABLE IF NOT EXISTS test_date (id Int8, d1 Date, d2 Date, d3 Date) ENGINE = MergeTree ORDER BY id"); + + try (Connection connV1 = getJdbcConnectionV1(null)) { + try (PreparedStatement stmtV1 = connV1.prepareStatement("INSERT INTO test_date VALUES (1, ?, ?, ?)")) {//INSERT with V1 + stmtV1.setDate(1, java.sql.Date.valueOf("2021-01-01")); + stmtV1.setDate(2, java.sql.Date.valueOf("2021-01-01"), new GregorianCalendar()); + stmtV1.setDate(3, java.sql.Date.valueOf("2021-01-01"), new GregorianCalendar(TimeZone.getTimeZone("UTC"))); + stmtV1.execute(); + } + } + + try (Connection connV1 = getJdbcConnectionV1(null); + Connection connV2 = getJdbcConnectionV2(null)) { + try (Statement stmtV1 = connV1.createStatement(); + Statement stmtV2 = connV2.createStatement()) { + ResultSet rsV1 = stmtV1.executeQuery("SELECT * FROM test_date"); + ResultSet rsV2 = stmtV2.executeQuery("SELECT * FROM test_date"); + assertTrue(rsV1.next()); + assertTrue(rsV2.next()); + + assertEquals(rsV2.getDate(2), rsV1.getDate(2)); + assertEquals(rsV2.getDate(3, new GregorianCalendar()), rsV1.getDate(3, new GregorianCalendar())); + assertEquals(rsV2.getDate(4, new GregorianCalendar(TimeZone.getTimeZone("UTC"))), + rsV1.getDate(4, new GregorianCalendar(TimeZone.getTimeZone("UTC")))); + } + } + } + + + @Test (groups = "integration", enabled = true) + public void setTimeTest() throws SQLException { + run("DROP TABLE IF EXISTS test_time"); + run("CREATE TABLE IF NOT EXISTS test_time (id Int8, t1 Datetime, t2 Datetime, t3 Datetime) ENGINE = MergeTree ORDER BY id"); + + try (Connection connV1 = getJdbcConnectionV1(null)) { + try (PreparedStatement stmtV1 = connV1.prepareStatement("INSERT INTO test_time VALUES (1, ?, ?, ?)")) {//INSERT with V1 + stmtV1.setTime(1, java.sql.Time.valueOf("12:34:56")); + stmtV1.setTime(2, java.sql.Time.valueOf("12:34:56"), new GregorianCalendar()); + stmtV1.setTime(3, java.sql.Time.valueOf("12:34:56"), new GregorianCalendar(TimeZone.getTimeZone("UTC"))); + stmtV1.execute(); + } + } + + try (Connection connV1 = getJdbcConnectionV1(null); + Connection connV2 = getJdbcConnectionV2(null)) { + try (Statement stmtV1 = connV1.createStatement(); + Statement stmtV2 = connV2.createStatement()) { + ResultSet rsV1 = stmtV1.executeQuery("SELECT * FROM test_time"); + ResultSet rsV2 = stmtV2.executeQuery("SELECT * FROM test_time"); + assertTrue(rsV1.next()); + assertTrue(rsV2.next()); + + assertEquals(rsV2.getTime(2), rsV1.getTime(2)); + assertEquals(rsV2.getTime(3, new GregorianCalendar()), rsV1.getTime(3, new GregorianCalendar())); + assertEquals(rsV2.getTime(4, new GregorianCalendar(TimeZone.getTimeZone("UTC"))), + rsV1.getTime(4, new GregorianCalendar(TimeZone.getTimeZone("UTC")))); + } + } + } + + @Test (groups = "integration", enabled = true) + public void setTimestampTest() throws SQLException { + run("DROP TABLE IF EXISTS test_timestamp"); + run("CREATE TABLE IF NOT EXISTS test_timestamp (id Int8, t1 Datetime64(3), t2 Datetime64(6), t3 Datetime64(9)) ENGINE = MergeTree ORDER BY id"); + + try (Connection connV1 = getJdbcConnectionV1(null)) { + try (PreparedStatement stmtV1 = connV1.prepareStatement("INSERT INTO test_timestamp VALUES (1, ?, ?, ?)")) {//INSERT with V1 + stmtV1.setTimestamp(1, java.sql.Timestamp.valueOf("2021-01-01 01:23:45")); + stmtV1.setTimestamp(2, java.sql.Timestamp.valueOf("2021-01-01 01:23:45"), new GregorianCalendar()); + stmtV1.setTimestamp(3, java.sql.Timestamp.valueOf("2021-01-01 01:23:45"), new GregorianCalendar(TimeZone.getTimeZone("UTC"))); + stmtV1.execute(); + } + } + + try (Connection connV1 = getJdbcConnectionV1(null); + Connection connV2 = getJdbcConnectionV2(null)) { + try (Statement stmtV1 = connV1.createStatement(); + Statement stmtV2 = connV2.createStatement()) { + ResultSet rsV1 = stmtV1.executeQuery("SELECT * FROM test_timestamp"); + ResultSet rsV2 = stmtV2.executeQuery("SELECT * FROM test_timestamp"); + assertTrue(rsV1.next()); + assertTrue(rsV2.next()); + + assertEquals(rsV2.getTimestamp(2), rsV1.getTimestamp(2)); + assertEquals(rsV2.getTimestamp(3, new GregorianCalendar()), rsV1.getTimestamp(3, new GregorianCalendar())); + assertEquals(rsV2.getTimestamp(4, new GregorianCalendar(TimeZone.getTimeZone("UTC"))), + rsV1.getTimestamp(4, new GregorianCalendar(TimeZone.getTimeZone("UTC")))); + } + } + } +} diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java index 73f7964a4..da77bc0bd 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -38,6 +38,7 @@ public class ResultSetImpl implements ResultSet, JdbcV2Wrapper { private boolean closed; private final StatementImpl parentStatement; private boolean wasNull; + private final Calendar defaultCalendar; public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, ClickHouseBinaryFormatReader reader) { this.parentStatement = parentStatement; @@ -46,6 +47,7 @@ public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, Clic this.metaData = new com.clickhouse.jdbc.metadata.ResultSetMetaData(this); this.closed = false; this.wasNull = false; + this.defaultCalendar = new GregorianCalendar(); } protected ResultSetImpl(ResultSetImpl resultSet) { @@ -55,6 +57,7 @@ protected ResultSetImpl(ResultSetImpl resultSet) { this.metaData = resultSet.metaData; this.closed = false; this.wasNull = false; + this.defaultCalendar = new GregorianCalendar(); } private void checkClosed() throws SQLException { @@ -1052,11 +1055,10 @@ public Date getDate(String columnLabel, Calendar cal) throws SQLException { } wasNull = false; - if (cal == null) { - cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));//Default to UTC - } - - return Date.valueOf(zdt.withZoneSameInstant(cal.getTimeZone().toZoneId()).toLocalDate()); + Calendar c = (Calendar) (cal != null ? cal : defaultCalendar).clone(); + c.clear(); + c.set(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth(), 0, 0, 0); + return new Date(c.getTimeInMillis()); } catch (Exception e) { throw ExceptionUtils.toSqlState(String.format("Method: getDate(\"%s\") encountered an exception.", columnLabel), String.format("SQL: [%s]", parentStatement.getLastSql()), e); } @@ -1078,11 +1080,10 @@ public Time getTime(String columnLabel, Calendar cal) throws SQLException { } wasNull = false; - if (cal == null) { - cal = new GregorianCalendar(); - } - - return Time.valueOf(zdt.withZoneSameInstant(cal.getTimeZone().toZoneId()).toLocalTime()); + Calendar c = (Calendar) (cal != null ? cal : defaultCalendar).clone(); + c.clear(); + c.set(1970, Calendar.JANUARY, 1, zdt.getHour(), zdt.getMinute(), zdt.getSecond()); + return new Time(c.getTimeInMillis()); } catch (Exception e) { throw ExceptionUtils.toSqlState(String.format("Method: getTime(\"%s\") encountered an exception.", columnLabel), String.format("SQL: [%s]", parentStatement.getLastSql()), e); } @@ -1104,13 +1105,12 @@ public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLExcept } wasNull = false; - if (cal == null) { - cal = new GregorianCalendar(); - } - - Timestamp ts = Timestamp.valueOf(zdt.withZoneSameInstant(cal.getTimeZone().toZoneId()).toLocalDateTime()); - ts.setNanos(zdt.getNano()); - return ts;//This assumes the response is in cal.getTimeZone() + Calendar c = (Calendar) (cal != null ? cal : defaultCalendar).clone(); + c.set(zdt.getYear(), zdt.getMonthValue() - 1, zdt.getDayOfMonth(), zdt.getHour(), zdt.getMinute(), + zdt.getSecond()); + Timestamp timestamp = new Timestamp(c.getTimeInMillis()); + timestamp.setNanos(zdt.getNano()); + return timestamp; } catch (Exception e) { throw ExceptionUtils.toSqlState(String.format("Method: getTimestamp(\"%s\") encountered an exception.", columnLabel), String.format("SQL: [%s]", parentStatement.getLastSql()), e); } From 2309e9c472a1e9ea8eeef022547c808d582fb984 Mon Sep 17 00:00:00 2001 From: Paultagoras Date: Wed, 22 Jan 2025 15:42:42 -0500 Subject: [PATCH 5/6] Same behavior between V1 and V2 --- .../comparison/DateTimeComparisonTest.java | 9 ++++- .../com/clickhouse/jdbc/ConnectionImpl.java | 3 ++ .../jdbc/PreparedStatementImpl.java | 21 ++++------ .../com/clickhouse/jdbc/ResultSetImpl.java | 11 +---- .../com/clickhouse/jdbc/DataTypeTests.java | 40 +++++++++---------- .../jdbc/PreparedStatementTest.java | 12 +++--- .../com/clickhouse/jdbc/StatementTest.java | 16 ++++---- 7 files changed, 52 insertions(+), 60 deletions(-) diff --git a/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/comparison/DateTimeComparisonTest.java b/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/comparison/DateTimeComparisonTest.java index 44d928e7a..c941d39b6 100644 --- a/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/comparison/DateTimeComparisonTest.java +++ b/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/comparison/DateTimeComparisonTest.java @@ -15,6 +15,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Timestamp; import java.util.GregorianCalendar; import java.util.Properties; import java.util.TimeZone; @@ -133,13 +134,16 @@ public void setTimeTest() throws SQLException { @Test (groups = "integration", enabled = true) public void setTimestampTest() throws SQLException { run("DROP TABLE IF EXISTS test_timestamp"); - run("CREATE TABLE IF NOT EXISTS test_timestamp (id Int8, t1 Datetime64(3), t2 Datetime64(6), t3 Datetime64(9)) ENGINE = MergeTree ORDER BY id"); + run("CREATE TABLE IF NOT EXISTS test_timestamp (id Int8, t1 Datetime64(3), t2 Datetime64(6), t3 Datetime64(9), t4 DateTime64(9)) ENGINE = MergeTree ORDER BY id"); + + Timestamp ts = new Timestamp(System.currentTimeMillis()); try (Connection connV1 = getJdbcConnectionV1(null)) { - try (PreparedStatement stmtV1 = connV1.prepareStatement("INSERT INTO test_timestamp VALUES (1, ?, ?, ?)")) {//INSERT with V1 + try (PreparedStatement stmtV1 = connV1.prepareStatement("INSERT INTO test_timestamp VALUES (1, ?, ?, ?, ?)")) {//INSERT with V1 stmtV1.setTimestamp(1, java.sql.Timestamp.valueOf("2021-01-01 01:23:45")); stmtV1.setTimestamp(2, java.sql.Timestamp.valueOf("2021-01-01 01:23:45"), new GregorianCalendar()); stmtV1.setTimestamp(3, java.sql.Timestamp.valueOf("2021-01-01 01:23:45"), new GregorianCalendar(TimeZone.getTimeZone("UTC"))); + stmtV1.setTimestamp(4, ts); stmtV1.execute(); } } @@ -157,6 +161,7 @@ public void setTimestampTest() throws SQLException { assertEquals(rsV2.getTimestamp(3, new GregorianCalendar()), rsV1.getTimestamp(3, new GregorianCalendar())); assertEquals(rsV2.getTimestamp(4, new GregorianCalendar(TimeZone.getTimeZone("UTC"))), rsV1.getTimestamp(4, new GregorianCalendar(TimeZone.getTimeZone("UTC")))); + assertEquals(rsV2.getTimestamp(5), rsV1.getTimestamp(5)); } } } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java index 6e80f7b82..e0a102cc6 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java @@ -30,6 +30,7 @@ import java.sql.ShardingKey; import java.sql.Statement; import java.sql.Struct; +import java.util.Calendar; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -52,6 +53,7 @@ public class ConnectionImpl implements Connection, JdbcV2Wrapper { private QuerySettings defaultQuerySettings; private final com.clickhouse.jdbc.metadata.DatabaseMetaData metadata; + protected final Calendar defaultCalendar; public ConnectionImpl(String url, Properties info) throws SQLException { try { @@ -80,6 +82,7 @@ public ConnectionImpl(String url, Properties info) throws SQLException { .serverSetting(ServerSettings.WAIT_END_OF_QUERY, "0"); this.metadata = new com.clickhouse.jdbc.metadata.DatabaseMetaData(this, false, url); + this.defaultCalendar = Calendar.getInstance(); } catch (SQLException e) { throw e; } catch (Exception e) { diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java index 8dbc31f79..0175a01e8 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java @@ -41,7 +41,6 @@ import java.util.Collection; import java.util.GregorianCalendar; import java.util.Map; -import java.util.TimeZone; public class PreparedStatementImpl extends StatementImpl implements PreparedStatement, JdbcV2Wrapper { private static final Logger LOG = LoggerFactory.getLogger(PreparedStatementImpl.class); @@ -52,6 +51,8 @@ public class PreparedStatementImpl extends StatementImpl implements PreparedStat public static final DateTimeFormatter DATETIME_FORMATTER = new DateTimeFormatterBuilder() .appendPattern("yyyy-MM-dd HH:mm:ss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).toFormatter(); + private final Calendar defaultCalendar; + String originalSql; String [] sqlSegments; Object [] parameters; @@ -68,6 +69,8 @@ public PreparedStatementImpl(ConnectionImpl connection, String sql) throws SQLEx } else { this.parameters = new Object[0]; } + + this.defaultCalendar = connection.defaultCalendar; } private String compileSql() { @@ -268,12 +271,8 @@ public ResultSetMetaData getMetaData() throws SQLException { @Override public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { checkClosed(); - if (cal == null) { - cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));//This says whatever date is in UTC - } - LocalDate d = x.toLocalDate(); - Calendar c = (Calendar) cal.clone(); + Calendar c = (Calendar) (cal != null ? cal : defaultCalendar).clone(); c.clear(); c.set(d.getYear(), d.getMonthValue() - 1, d.getDayOfMonth(), 0, 0, 0); parameters[parameterIndex - 1] = encodeObject(c.toInstant()); @@ -282,12 +281,9 @@ public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLExceptio @Override public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { checkClosed(); - if (cal == null) { - cal = new GregorianCalendar(); - } LocalTime t = x.toLocalTime(); - Calendar c = (Calendar) cal.clone(); + Calendar c = (Calendar) (cal != null ? cal : defaultCalendar).clone(); c.clear(); c.set(1970, Calendar.JANUARY, 1, t.getHour(), t.getMinute(), t.getSecond()); parameters[parameterIndex - 1] = encodeObject(c.toInstant()); @@ -296,12 +292,9 @@ public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLExceptio @Override public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { checkClosed(); - if (cal == null) { - cal = new GregorianCalendar(); - } LocalDateTime ldt = x.toLocalDateTime(); - Calendar c = (Calendar) cal.clone(); + Calendar c = (Calendar) (cal != null ? cal : defaultCalendar).clone(); c.clear(); c.set(ldt.getYear(), ldt.getMonthValue() - 1, ldt.getDayOfMonth(), ldt.getHour(), ldt.getMinute(), ldt.getSecond()); parameters[parameterIndex - 1] = encodeObject(c.toInstant().atZone(ZoneId.of("UTC")).withNano(x.getNanos())); diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java index da77bc0bd..5362bec98 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -1,24 +1,17 @@ package com.clickhouse.jdbc; import java.io.ByteArrayInputStream; -import java.io.CharArrayReader; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.math.BigDecimal; -import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.sql.*; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Map; -import java.util.TimeZone; import com.clickhouse.client.api.data_formats.ClickHouseBinaryFormatReader; import com.clickhouse.client.api.metadata.TableSchema; @@ -47,7 +40,7 @@ public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, Clic this.metaData = new com.clickhouse.jdbc.metadata.ResultSetMetaData(this); this.closed = false; this.wasNull = false; - this.defaultCalendar = new GregorianCalendar(); + this.defaultCalendar = parentStatement.connection.defaultCalendar; } protected ResultSetImpl(ResultSetImpl resultSet) { @@ -57,7 +50,7 @@ protected ResultSetImpl(ResultSetImpl resultSet) { this.metaData = resultSet.metaData; this.closed = false; this.wasNull = false; - this.defaultCalendar = new GregorianCalendar(); + this.defaultCalendar = parentStatement.connection.defaultCalendar; } private void checkClosed() throws SQLException { diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java index 1e27f19b6..7868ccbbd 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java @@ -283,29 +283,29 @@ public void testDateTypes() throws SQLException { assertTrue(rs.next()); assertEquals(rs.getDate("date"), Date.valueOf("1970-01-01")); assertEquals(rs.getDate("date32"), Date.valueOf("1970-01-01")); - assertEquals(rs.getTimestamp("dateTime").toInstant().toString(), "1970-01-01T00:00:00Z"); - assertEquals(rs.getTimestamp("dateTime32").toInstant().toString(), "1970-01-01T00:00:00Z"); - assertEquals(rs.getTimestamp("dateTime643").toInstant().toString(), "1970-01-01T00:00:00Z"); - assertEquals(rs.getTimestamp("dateTime646").toInstant().toString(), "1970-01-01T00:00:00Z"); - assertEquals(rs.getTimestamp("dateTime649").toInstant().toString(), "1970-01-01T00:00:00Z"); + assertEquals(rs.getTimestamp("dateTime").toString(), "1970-01-01 00:00:00.0"); + assertEquals(rs.getTimestamp("dateTime32").toString(), "1970-01-01 00:00:00.0"); + assertEquals(rs.getTimestamp("dateTime643").toString(), "1970-01-01 00:00:00.0"); + assertEquals(rs.getTimestamp("dateTime646").toString(), "1970-01-01 00:00:00.0"); + assertEquals(rs.getTimestamp("dateTime649").toString(), "1970-01-01 00:00:00.0"); assertTrue(rs.next()); assertEquals(rs.getDate("date"), Date.valueOf("2149-06-06")); assertEquals(rs.getDate("date32"), Date.valueOf("2299-12-31")); - assertEquals(rs.getTimestamp("dateTime").toInstant().toString(), "2106-02-07T06:28:15Z"); - assertEquals(rs.getTimestamp("dateTime32").toInstant().toString(), "2106-02-07T06:28:15Z"); - assertEquals(rs.getTimestamp("dateTime643").toInstant().toString(), "2261-12-31T23:59:59.999Z"); - assertEquals(rs.getTimestamp("dateTime646").toInstant().toString(), "2261-12-31T23:59:59.999999Z"); - assertEquals(rs.getTimestamp("dateTime649").toInstant().toString(), "2261-12-31T23:59:59.999999999Z"); + assertEquals(rs.getTimestamp("dateTime").toString(), "2106-02-07 06:28:15.0"); + assertEquals(rs.getTimestamp("dateTime32").toString(), "2106-02-07 06:28:15.0"); + assertEquals(rs.getTimestamp("dateTime643").toString(), "2261-12-31 23:59:59.999"); + assertEquals(rs.getTimestamp("dateTime646").toString(), "2261-12-31 23:59:59.999999"); + assertEquals(rs.getTimestamp("dateTime649").toString(), "2261-12-31 23:59:59.999999999"); assertTrue(rs.next()); assertEquals(rs.getDate("date").toString(), date.toString()); assertEquals(rs.getDate("date32").toString(), date32.toString()); - assertEquals(rs.getTimestamp("dateTime").toString(), dateTime.toString()); - assertEquals(rs.getTimestamp("dateTime32").toString(), dateTime32.toString()); - assertEquals(rs.getTimestamp("dateTime643").toString(), dateTime643.toString()); - assertEquals(rs.getTimestamp("dateTime646").toString(), dateTime646.toString()); - assertEquals(rs.getTimestamp("dateTime649").toString(), dateTime649.toString()); + assertEquals(rs.getTimestamp("dateTime", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), dateTime.toString()); + assertEquals(rs.getTimestamp("dateTime32", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), dateTime32.toString()); + assertEquals(rs.getTimestamp("dateTime643", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), dateTime643.toString()); + assertEquals(rs.getTimestamp("dateTime646", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), dateTime646.toString()); + assertEquals(rs.getTimestamp("dateTime649", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), dateTime649.toString()); assertFalse(rs.next()); } @@ -887,7 +887,7 @@ public void testTypeConversions() throws Exception { assertEquals(rs.getObject(3, Double.class), 1.0); assertEquals(String.valueOf(rs.getObject(3, new HashMap>(){{put(JDBCType.FLOAT.getName(), Float.class);}})), "1.0"); - assertEquals(rs.getDate(4, new GregorianCalendar(TimeZone.getTimeZone("UTC"))), Date.valueOf("2024-12-01")); + assertEquals(rs.getDate(4), Date.valueOf("2024-12-01")); assertTrue(rs.getObject(4) instanceof Date); assertEquals(rs.getObject(4), Date.valueOf("2024-12-01")); assertEquals(rs.getString(4), "2024-12-01");//Underlying object is ZonedDateTime @@ -895,7 +895,7 @@ public void testTypeConversions() throws Exception { assertEquals(rs.getObject(4, ZonedDateTime.class), ZonedDateTime.of(2024, 12, 1, 0, 0, 0, 0, ZoneId.of("UTC"))); assertEquals(String.valueOf(rs.getObject(4, new HashMap>(){{put(JDBCType.DATE.getName(), LocalDate.class);}})), "2024-12-01"); - assertEquals(rs.getTimestamp(5).toInstant().toString(), "2024-12-01T12:34:56Z"); + assertEquals(rs.getTimestamp(5).toString(), "2024-12-01 12:34:56.0"); assertTrue(rs.getObject(5) instanceof Timestamp); assertEquals(rs.getObject(5), Timestamp.valueOf("2024-12-01 12:34:56")); assertEquals(rs.getString(5), "2024-12-01T12:34:56Z[UTC]"); @@ -903,21 +903,21 @@ public void testTypeConversions() throws Exception { assertEquals(rs.getObject(5, ZonedDateTime.class), ZonedDateTime.of(2024, 12, 1, 12, 34, 56, 0, ZoneId.of("UTC"))); assertEquals(String.valueOf(rs.getObject(5, new HashMap>(){{put(JDBCType.TIMESTAMP.getName(), LocalDateTime.class);}})), "2024-12-01T12:34:56"); - assertEquals(rs.getTimestamp(6).toInstant().toString(), "2024-12-01T12:34:56.789Z"); + assertEquals(rs.getTimestamp(6).toString(), "2024-12-01 12:34:56.789"); assertTrue(rs.getObject(6) instanceof Timestamp); assertEquals(rs.getObject(6), Timestamp.valueOf("2024-12-01 12:34:56.789")); assertEquals(rs.getString(6), "2024-12-01T12:34:56.789Z[UTC]"); assertEquals(rs.getObject(6, LocalDateTime.class), LocalDateTime.of(2024, 12, 1, 12, 34, 56, 789000000)); assertEquals(String.valueOf(rs.getObject(6, new HashMap>(){{put(JDBCType.TIMESTAMP.getName(), LocalDateTime.class);}})), "2024-12-01T12:34:56.789"); - assertEquals(rs.getTimestamp(7).toInstant().toString(), "2024-12-01T12:34:56.789789Z"); + assertEquals(rs.getTimestamp(7).toString(), "2024-12-01 12:34:56.789789"); assertTrue(rs.getObject(7) instanceof Timestamp); assertEquals(rs.getObject(7), Timestamp.valueOf("2024-12-01 12:34:56.789789")); assertEquals(rs.getString(7), "2024-12-01T12:34:56.789789Z[UTC]"); assertEquals(rs.getObject(7, LocalDateTime.class), LocalDateTime.of(2024, 12, 1, 12, 34, 56, 789789000)); assertEquals(String.valueOf(rs.getObject(7, new HashMap>(){{put(JDBCType.TIMESTAMP.getName(), OffsetDateTime.class);}})), "2024-12-01T12:34:56.789789Z"); - assertEquals(rs.getTimestamp(8).toInstant().toString(), "2024-12-01T12:34:56.789789789Z"); + assertEquals(rs.getTimestamp(8).toString(), "2024-12-01 12:34:56.789789789"); assertTrue(rs.getObject(8) instanceof Timestamp); assertEquals(rs.getObject(8), Timestamp.valueOf("2024-12-01 12:34:56.789789789")); assertEquals(rs.getString(8), "2024-12-01T12:34:56.789789789Z[UTC]"); diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java index 0fb953eb8..a84ef2289 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java @@ -170,10 +170,10 @@ public void testSetBytes() throws Exception { public void testSetDate() throws Exception { try (Connection conn = getJdbcConnection()) { try (PreparedStatement stmt = conn.prepareStatement("SELECT toDate(?)")) { - stmt.setDate(1, java.sql.Date.valueOf("2021-01-01")); + stmt.setDate(1, java.sql.Date.valueOf("2021-01-01"), new GregorianCalendar(TimeZone.getTimeZone("UTC"))); try (ResultSet rs = stmt.executeQuery()) { assertTrue(rs.next()); - assertEquals(rs.getDate(1, new GregorianCalendar(TimeZone.getTimeZone("UTC"))), java.sql.Date.valueOf("2021-01-01")); + assertEquals(rs.getDate(1), java.sql.Date.valueOf("2021-01-01")); assertFalse(rs.next()); } } @@ -184,7 +184,7 @@ public void testSetDate() throws Exception { public void testSetTime() throws Exception { try (Connection conn = getJdbcConnection()) { try (PreparedStatement stmt = conn.prepareStatement("SELECT toDateTime(?)")) { - stmt.setTime(1, java.sql.Time.valueOf("12:34:56")); + stmt.setTime(1, java.sql.Time.valueOf("12:34:56"), new GregorianCalendar(TimeZone.getTimeZone("UTC"))); try (ResultSet rs = stmt.executeQuery()) { assertTrue(rs.next()); assertEquals(rs.getTime(1).toString(), "12:34:56"); @@ -197,13 +197,11 @@ public void testSetTime() throws Exception { @Test(groups = { "integration" }) public void testSetTimestamp() throws Exception { try (Connection conn = getJdbcConnection()) { - try (PreparedStatement stmt = conn.prepareStatement("SELECT toDateTime64(?, 3), toDateTime64(?, 3)")) { - stmt.setTimestamp(1, java.sql.Timestamp.valueOf("2021-01-01 01:34:56.456")); - stmt.setTimestamp(2, java.sql.Timestamp.valueOf("2021-01-01 01:34:56.456"), java.util.Calendar.getInstance(java.util.TimeZone.getTimeZone("UTC"))); + try (PreparedStatement stmt = conn.prepareStatement("SELECT toDateTime64(?, 3)")) { + stmt.setTimestamp(1, java.sql.Timestamp.valueOf("2021-01-01 01:34:56.456"), new GregorianCalendar(TimeZone.getTimeZone("UTC"))); try (ResultSet rs = stmt.executeQuery()) { assertTrue(rs.next()); assertEquals(rs.getTimestamp(1).toString(), "2021-01-01 01:34:56.456"); - assertEquals(rs.getTimestamp(2, java.util.Calendar.getInstance(java.util.TimeZone.getTimeZone("UTC"))).toString(), "2021-01-01 01:34:56.456"); assertFalse(rs.next()); } } diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java index 1ca7db374..6ac7f5086 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java @@ -127,12 +127,12 @@ public void testExecuteQueryDates() throws Exception { try (Statement stmt = conn.createStatement()) { try (ResultSet rs = stmt.executeQuery("SELECT toDate('2020-01-01') AS date, toDateTime('2020-01-01 10:11:12', 'Asia/Istanbul') AS datetime")) { assertTrue(rs.next()); - assertEquals(rs.getDate(1, new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), Date.valueOf("2020-01-01").toString()); - assertEquals(rs.getDate("date", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), Date.valueOf("2020-01-01").toString()); + assertEquals(rs.getDate(1).toString(), Date.valueOf("2020-01-01").toString()); + assertEquals(rs.getDate("date").toString(), Date.valueOf("2020-01-01").toString()); assertEquals(rs.getString(1), "2020-01-01"); assertEquals(rs.getString("date"), "2020-01-01"); - assertEquals(rs.getDate(2, new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), "2020-01-01"); - assertEquals(rs.getDate("datetime", new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), "2020-01-01"); + assertEquals(rs.getDate(2).toString(), "2020-01-01"); + assertEquals(rs.getDate("datetime").toString(), "2020-01-01"); assertEquals(rs.getString(2), "2020-01-01T10:11:12+03:00[Asia/Istanbul]"); assertEquals(rs.getString("datetime"), "2020-01-01T10:11:12+03:00[Asia/Istanbul]"); assertFalse(rs.next()); @@ -244,13 +244,13 @@ public void testExecuteUpdateDates() throws Exception { assertEquals(stmt.executeUpdate("INSERT INTO " + getDatabase() + ".dates VALUES (0, '2020-01-01', '2020-01-01 10:11:12'), (1, NULL, '2020-01-01 12:10:07'), (2, '2020-01-01', NULL)"), 3); try (ResultSet rs = stmt.executeQuery("SELECT date, datetime FROM " + getDatabase() + ".dates ORDER BY id")) { assertTrue(rs.next()); - assertEquals(rs.getDate(1, new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), "2020-01-01"); - assertEquals(rs.getDate(2, new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), "2020-01-01"); + assertEquals(rs.getDate(1).toString(), "2020-01-01"); + assertEquals(rs.getDate(2).toString(), "2020-01-01"); assertTrue(rs.next()); assertNull(rs.getDate(1)); assertEquals(rs.getDate(2).toString(), "2020-01-01"); assertTrue(rs.next()); - assertEquals(rs.getDate(1, new GregorianCalendar(TimeZone.getTimeZone("UTC"))).toString(), "2020-01-01"); + assertEquals(rs.getDate(1).toString(), "2020-01-01"); assertNull(rs.getDate(2)); assertFalse(rs.next()); } @@ -300,7 +300,7 @@ public void testJdbcEscapeSyntax() throws Exception { "toInt32({fn LENGTH('Hello')}) AS FNLENGTH, toInt32({fn POSITION('Hello', 'l')}) AS FNPOSITION, toInt32({fn MOD(10, 3)}) AS FNMOD, " + "{fn SQRT(9)} AS FNSQRT, {fn SUBSTRING('Hello', 3, 2)} AS FNSUBSTRING")) { assertTrue(rs.next()); - assertEquals(rs.getDate(1, new GregorianCalendar(TimeZone.getTimeZone("UTC"))), Date.valueOf(LocalDate.of(2021, 11, 1))); + assertEquals(rs.getDate(1), Date.valueOf(LocalDate.of(2021, 11, 1))); //assertEquals(rs.getTimestamp(2), java.sql.Timestamp.valueOf(LocalDateTime.of(2021, 11, 1, 12, 34, 56))); assertEquals(rs.getInt(3), 1); assertEquals(rs.getInt("FNABS"), 1); From 3f57bc5a23ac1b6f014ea1f821096928bc456b63 Mon Sep 17 00:00:00 2001 From: Paultagoras Date: Thu, 23 Jan 2025 11:37:43 -0500 Subject: [PATCH 6/6] Update PreparedStatementImpl.java --- .../main/java/com/clickhouse/jdbc/PreparedStatementImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java index 0175a01e8..e8715b62f 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java @@ -32,6 +32,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -472,6 +473,8 @@ private static String encodeObject(Object x) throws SQLException { return "'" + DATETIME_FORMATTER.format(((Timestamp) x).toLocalDateTime()) + "'"; } else if (x instanceof LocalDateTime) { return "'" + DATETIME_FORMATTER.format((LocalDateTime) x) + "'"; + } else if (x instanceof OffsetDateTime) { + return encodeObject(((OffsetDateTime) x).toInstant()); } else if (x instanceof ZonedDateTime) { return encodeObject(((ZonedDateTime) x).toInstant()); } else if (x instanceof Instant) {