Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,7 @@ private static String getDataTypeInfoSql() {

sql.append(") as attrs ON (dt.name = attrs.c1)")
.append(" WHERE alias_to == ''");
sql.append(" ORDER BY name");
return sql.toString();
}

Expand Down
101 changes: 100 additions & 1 deletion jdbc-v2/src/test/java/com/clickhouse/jdbc/JdbcDataTypeTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
Expand Down Expand Up @@ -2313,4 +2312,104 @@ public Object[][] testJSONReadDP() {

};
}

/**
* Tests that both Time and DateTime columns are readable as JDBC TIME type.
* ClickHouse added Time and Time64 support in version 25.6.
* On older versions DateTime types were used to emulate TIME.
* This test ensures compatibility for reading time values from both column types.
*/
@Test(groups = { "integration" })
public void testTimeAndDateTimeCompatibleWithJDBCTime() throws Exception {
boolean hasTimeType = !ClickHouseVersion.of(getServerVersion()).check("(,25.5]");

Properties createProperties = new Properties();
if (hasTimeType) {
createProperties.put(ClientConfigProperties.serverSetting("allow_experimental_time_time64_type"), "1");
}

// Create table with DateTime columns (always supported) and Time columns (if available)
String tableDDL = hasTimeType
? "CREATE TABLE test_time_compat (order Int8, "
+ "time Time, time64 Time64(3), "
+ "dateTime DateTime('UTC'), dateTime64 DateTime64(3, 'UTC') "
+ ") ENGINE = MergeTree ORDER BY ()"
: "CREATE TABLE test_time_compat (order Int8, "
+ "dateTime DateTime('UTC'), dateTime64 DateTime64(3, 'UTC') "
+ ") ENGINE = MergeTree ORDER BY ()";

runQuery(tableDDL, createProperties);

// Insert values representing times: 12:34:56 and 23:59:59.999
String insertSQL = hasTimeType
? "INSERT INTO test_time_compat (order, time, time64, dateTime, dateTime64) VALUES "
+ "(1, '12:34:56', '12:34:56.789', '1970-01-01 12:34:56', '1970-01-01 12:34:56.789'), "
+ "(2, '23:59:59', '23:59:59.999', '1970-01-01 23:59:59', '1970-01-01 23:59:59.999')"
: "INSERT INTO test_time_compat (order, dateTime, dateTime64) VALUES "
+ "(1, '1970-01-01 12:34:56', '1970-01-01 12:34:56.789'), "
+ "(2, '1970-01-01 23:59:59', '1970-01-01 23:59:59.999')";

runQuery(insertSQL, createProperties);

// Expected values for each row: [order, year, month, day, hours, minutes, seconds, milliseconds]
// Note: month is 1-based (1 = January)
int[][] expectedValues = {
{1, 1970, 1, 1, 12, 34, 56, 789},
{2, 1970, 1, 1, 23, 59, 59, 999}
};

Calendar utcCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));

// Check that all columns are readable as java.sql.Time and verify date components
try (Connection conn = getJdbcConnection()) {
try (Statement stmt = conn.createStatement()) {
try (ResultSet rs = stmt.executeQuery("SELECT * FROM test_time_compat ORDER BY order")) {
for (int[] expected : expectedValues) {
assertTrue(rs.next());
assertEquals(rs.getInt("order"), expected[0]);

// Test DateTime columns as Time (always available)
verifyTimeValue(rs.getTime("dateTime", utcCalendar), expected[4], expected[5], expected[6], 0, utcCalendar);
verifyTimeValue(rs.getTime("dateTime64", utcCalendar), expected[4], expected[5], expected[6], expected[7], utcCalendar);

// Verify date components for DateTime columns
verifyDateValue(rs.getDate("dateTime", utcCalendar), expected[1], expected[2], expected[3], utcCalendar);
verifyDateValue(rs.getDate("dateTime64", utcCalendar), expected[1], expected[2], expected[3], utcCalendar);

if (hasTimeType) {
// Test Time columns as Time
verifyTimeValue(rs.getTime("time", utcCalendar), expected[4], expected[5], expected[6], 0, utcCalendar);
verifyTimeValue(rs.getTime("time64", utcCalendar), expected[4], expected[5], expected[6], expected[7], utcCalendar);
}
}
assertFalse(rs.next());
}
}
}
}

private static void assertNotNull(Object obj) {
Assert.assertNotNull(obj);
}

private static void verifyTimeValue(Time time, int expectedHours, int expectedMinutes,
int expectedSeconds, int expectedMillis, Calendar calendar) {
assertNotNull(time);
calendar.setTime(time);
assertEquals(calendar.get(Calendar.HOUR_OF_DAY), expectedHours);
assertEquals(calendar.get(Calendar.MINUTE), expectedMinutes);
assertEquals(calendar.get(Calendar.SECOND), expectedSeconds);
if (expectedMillis > 0) {
assertEquals(time.getTime() % 1000, expectedMillis);
}
}

private static void verifyDateValue(Date date, int expectedYear, int expectedMonth,
int expectedDay, Calendar calendar) {
assertNotNull(date);
calendar.setTime(date);
assertEquals(calendar.get(Calendar.YEAR), expectedYear);
assertEquals(calendar.get(Calendar.MONTH) + 1, expectedMonth); // Calendar.MONTH is 0-based, convert to 1-based
assertEquals(calendar.get(Calendar.DAY_OF_MONTH), expectedDay);
}
}
Loading