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 41dbb6024..dd45e1a24 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java @@ -487,7 +487,7 @@ public Properties getClientInfo() throws SQLException { @Override public Array createArrayOf(String typeName, Object[] elements) throws SQLException { try { - return new com.clickhouse.jdbc.types.Array(List.of(elements), typeName, JdbcUtils.convertToSqlType(ClickHouseDataType.valueOf(typeName))); + return new com.clickhouse.jdbc.types.Array(List.of(elements), typeName, JdbcUtils.convertToSqlType(ClickHouseDataType.valueOf(typeName)).getVendorTypeNumber()); } catch (Exception e) { throw new SQLException("Failed to create array", ExceptionUtils.SQL_STATE_CLIENT_ERROR, e); } 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 290c2499f..69f6ec4ba 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -615,34 +615,12 @@ public ResultSetMetaData getMetaData() throws SQLException { @Override public Object getObject(int columnIndex) throws SQLException { - checkClosed(); - try { - if (reader.hasValue(columnIndex)) { - wasNull = false; - return reader.readValue(columnIndex); - } else { - wasNull = true; - return null; - } - } catch (Exception e) { - throw ExceptionUtils.toSqlState(String.format("SQL: [%s]; Method: getObject(%s)", parentStatement.getLastSql(), columnIndex), e); - } + return getObject(getSchema().columnIndexToName(columnIndex)); } @Override public Object getObject(String columnLabel) throws SQLException { - checkClosed(); - try { - if (reader.hasValue(columnLabel)) { - wasNull = false; - return reader.readValue(columnLabel); - } else { - wasNull = true; - return null; - } - } catch (Exception e) { - throw ExceptionUtils.toSqlState(String.format("SQL: [%s]; Method: getObject(%s)", parentStatement.getLastSql(), columnLabel), e); - } + return getObject(columnLabel, JdbcUtils.convertToJavaClass(getSchema().getColumnByName(columnLabel).getDataType())); } @Override @@ -888,154 +866,97 @@ public boolean rowDeleted() throws SQLException { @Override public void updateNull(int columnIndex) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateNull(getSchema().columnIndexToName(columnIndex)); } @Override public void updateBoolean(int columnIndex, boolean x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateBoolean(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateByte(int columnIndex, byte x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateByte(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateShort(int columnIndex, short x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateShort(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateInt(int columnIndex, int x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateInt(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateLong(int columnIndex, long x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateLong(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateFloat(int columnIndex, float x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateFloat(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateDouble(int columnIndex, double x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateDouble(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateBigDecimal(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateString(int columnIndex, String x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateString(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateBytes(int columnIndex, byte[] x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateBytes(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateDate(int columnIndex, Date x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateDate(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateTime(int columnIndex, Time x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateTime(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateTimestamp(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateAsciiStream(getSchema().columnIndexToName(columnIndex), x, length); } @Override public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateBinaryStream(getSchema().columnIndexToName(columnIndex), x, length); } @Override public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateCharacterStream(getSchema().columnIndexToName(columnIndex), x, length); } @Override public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateObject(getSchema().columnIndexToName(columnIndex), x, scaleOrLength); } @Override public void updateObject(int columnIndex, Object x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateObject(getSchema().columnIndexToName(columnIndex), x); } @Override @@ -1233,50 +1154,33 @@ public Statement getStatement() throws SQLException { @Override public Object getObject(int columnIndex, Map> map) throws SQLException { - checkClosed(); - return getObject(columnIndex); + return getObject(getSchema().columnIndexToName(columnIndex), map); } @Override public Ref getRef(int columnIndex) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Ref is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } - - return null; + return getRef(getSchema().columnIndexToName(columnIndex)); } @Override public Blob getBlob(int columnIndex) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Blob is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } - - return null; + return getBlob(getSchema().columnIndexToName(columnIndex)); } @Override public java.sql.Clob getClob(int columnIndex) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Clob is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } - - return null; + return getClob(getSchema().columnIndexToName(columnIndex)); } @Override public java.sql.Array getArray(int columnIndex) throws SQLException { - checkClosed(); - return getArray(reader.getSchema().columnIndexToName(columnIndex)); + return getArray(getSchema().columnIndexToName(columnIndex)); } @Override public Object getObject(String columnLabel, Map> map) throws SQLException { checkClosed(); - return getObject(columnLabel); + return getObject(columnLabel, map.get(JdbcUtils.convertToSqlType(getSchema().getColumnByName(columnLabel).getDataType()).getName())); } @Override @@ -1313,10 +1217,10 @@ public Clob getClob(String columnLabel) throws SQLException { public java.sql.Array getArray(String columnLabel) throws SQLException { checkClosed(); try { - ClickHouseColumn column = reader.getSchema().getColumnByName(columnLabel); + ClickHouseColumn column = getSchema().getColumnByName(columnLabel); return new Array(reader.getList(columnLabel), column.getArrayBaseColumn().getDataType().name(), - JdbcUtils.convertToSqlType(column.getArrayBaseColumn().getDataType())); + JdbcUtils.convertToSqlType(column.getArrayBaseColumn().getDataType()).getVendorTypeNumber()); } catch (Exception e) { throw ExceptionUtils.toSqlState(String.format("SQL: [%s]; Method: getArray(%s)", parentStatement.getLastSql(), columnLabel), e); } @@ -1324,8 +1228,7 @@ public java.sql.Array getArray(String columnLabel) throws SQLException { @Override public Date getDate(int columnIndex, Calendar cal) throws SQLException { - checkClosed(); - return getDate(columnIndex); + return getDate(getSchema().columnIndexToName(columnIndex), cal); } @Override @@ -1336,8 +1239,7 @@ public Date getDate(String columnLabel, Calendar cal) throws SQLException { @Override public Time getTime(int columnIndex, Calendar cal) throws SQLException { - checkClosed(); - return getTime(columnIndex); + return getTime(getSchema().columnIndexToName(columnIndex), cal); } @Override @@ -1348,8 +1250,7 @@ public Time getTime(String columnLabel, Calendar cal) throws SQLException { @Override public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { - checkClosed(); - return getTimestamp(columnIndex); + return getTimestamp(getSchema().columnIndexToName(columnIndex), cal); } @Override @@ -1360,12 +1261,7 @@ public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLExcept @Override public URL getURL(int columnIndex) throws SQLException { - checkClosed(); - try { - return new URL(reader.getString(columnIndex)); - } catch (MalformedURLException e) { - throw new SQLDataException(e); - } + return getURL(getSchema().columnIndexToName(columnIndex)); } @Override @@ -1380,10 +1276,7 @@ public URL getURL(String columnLabel) throws SQLException { @Override public void updateRef(int columnIndex, Ref x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateRef(getSchema().columnIndexToName(columnIndex), x); } @Override @@ -1396,10 +1289,7 @@ public void updateRef(String columnLabel, Ref x) throws SQLException { @Override public void updateBlob(int columnIndex, Blob x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateBlob(getSchema().columnIndexToName(columnIndex), x); } @Override @@ -1412,10 +1302,7 @@ public void updateBlob(String columnLabel, Blob x) throws SQLException { @Override public void updateClob(int columnIndex, Clob x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateClob(getSchema().columnIndexToName(columnIndex), x); } @Override @@ -1428,10 +1315,7 @@ public void updateClob(String columnLabel, Clob x) throws SQLException { @Override public void updateArray(int columnIndex, java.sql.Array x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateArray(getSchema().columnIndexToName(columnIndex), x); } @Override @@ -1444,8 +1328,7 @@ public void updateArray(String columnLabel, java.sql.Array x) throws SQLExceptio @Override public RowId getRowId(int columnIndex) throws SQLException { - checkClosed(); - return null; + return getRowId(getSchema().columnIndexToName(columnIndex)); } @Override @@ -1456,10 +1339,7 @@ public RowId getRowId(String columnLabel) throws SQLException { @Override public void updateRowId(int columnIndex, RowId x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateRowId(getSchema().columnIndexToName(columnIndex), x); } @Override @@ -1483,10 +1363,7 @@ public boolean isClosed() throws SQLException { @Override public void updateNString(int columnIndex, String nString) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateNString(getSchema().columnIndexToName(columnIndex), nString); } @Override @@ -1499,10 +1376,7 @@ public void updateNString(String columnLabel, String nString) throws SQLExceptio @Override public void updateNClob(int columnIndex, NClob nClob) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateNClob(getSchema().columnIndexToName(columnIndex), nClob); } @Override @@ -1515,12 +1389,7 @@ public void updateNClob(String columnLabel, NClob nClob) throws SQLException { @Override public NClob getNClob(int columnIndex) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("NClob is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } - - return null; + return getNClob(getSchema().columnIndexToName(columnIndex)); } @Override @@ -1535,12 +1404,7 @@ public NClob getNClob(String columnLabel) throws SQLException { @Override public SQLXML getSQLXML(int columnIndex) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("SQLXML is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } - - return null; + return getSQLXML(getSchema().columnIndexToName(columnIndex)); } @Override @@ -1555,10 +1419,7 @@ public SQLXML getSQLXML(String columnLabel) throws SQLException { @Override public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateSQLXML(getSchema().columnIndexToName(columnIndex), xmlObject); } @Override @@ -1571,8 +1432,7 @@ public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLExcepti @Override public String getNString(int columnIndex) throws SQLException { - checkClosed(); - return reader.getString(columnIndex); + return getNString(getSchema().columnIndexToName(columnIndex)); } @Override @@ -1583,8 +1443,7 @@ public String getNString(String columnLabel) throws SQLException { @Override public Reader getNCharacterStream(int columnIndex) throws SQLException { - checkClosed(); - return new CharArrayReader(reader.getString(columnIndex).toCharArray()); + return getNCharacterStream(getSchema().columnIndexToName(columnIndex)); } @Override @@ -1595,10 +1454,7 @@ public Reader getNCharacterStream(String columnLabel) throws SQLException { @Override public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateNCharacterStream(getSchema().columnIndexToName(columnIndex), x, length); } @Override @@ -1611,26 +1467,17 @@ public void updateNCharacterStream(String columnLabel, Reader reader, long lengt @Override public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateAsciiStream(getSchema().columnIndexToName(columnIndex), x, length); } @Override public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateBinaryStream(getSchema().columnIndexToName(columnIndex), x, length); } @Override public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateCharacterStream(getSchema().columnIndexToName(columnIndex), x, length); } @Override @@ -1659,10 +1506,7 @@ public void updateCharacterStream(String columnLabel, Reader reader, long length @Override public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateBlob(getSchema().columnIndexToName(columnIndex), inputStream, length); } @Override @@ -1675,10 +1519,7 @@ public void updateBlob(String columnLabel, InputStream inputStream, long length) @Override public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateClob(getSchema().columnIndexToName(columnIndex), reader, length); } @Override @@ -1691,10 +1532,7 @@ public void updateClob(String columnLabel, Reader reader, long length) throws SQ @Override public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateNClob(getSchema().columnIndexToName(columnIndex), reader, length); } @Override @@ -1707,10 +1545,7 @@ public void updateNClob(String columnLabel, Reader reader, long length) throws S @Override public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateNCharacterStream(getSchema().columnIndexToName(columnIndex), x); } @Override @@ -1723,26 +1558,17 @@ public void updateNCharacterStream(String columnLabel, Reader reader) throws SQL @Override public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateAsciiStream(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateBinaryStream(getSchema().columnIndexToName(columnIndex), x); } @Override public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateCharacterStream(getSchema().columnIndexToName(columnIndex), x); } @Override @@ -1771,10 +1597,7 @@ public void updateCharacterStream(String columnLabel, Reader reader) throws SQLE @Override public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateBlob(getSchema().columnIndexToName(columnIndex), inputStream); } @Override @@ -1787,10 +1610,7 @@ public void updateBlob(String columnLabel, InputStream inputStream) throws SQLEx @Override public void updateClob(int columnIndex, Reader reader) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateClob(getSchema().columnIndexToName(columnIndex), reader); } @Override @@ -1803,10 +1623,7 @@ public void updateClob(String columnLabel, Reader reader) throws SQLException { @Override public void updateNClob(int columnIndex, Reader reader) throws SQLException { - checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + updateNClob(getSchema().columnIndexToName(columnIndex), reader); } @Override @@ -1819,19 +1636,24 @@ public void updateNClob(String columnLabel, Reader reader) throws SQLException { @Override public T getObject(int columnIndex, Class type) throws SQLException { - checkClosed(); - try { - return (T) reader.readValue(columnIndex); - } catch (Exception e) { - throw ExceptionUtils.toSqlState(e); - } + return getObject(getSchema().columnIndexToName(columnIndex), type); } @Override public T getObject(String columnLabel, Class type) throws SQLException { checkClosed(); try { - return (T) reader.readValue(columnLabel); + if (reader.hasValue(columnLabel)) { + wasNull = false; + if (type == null) {//As a fallback, try to get the value as is + return reader.readValue(columnLabel); + } + + return (T) JdbcUtils.convert(reader.readValue(columnLabel), type); + } else { + wasNull = true; + return null; + } } catch (Exception e) { throw ExceptionUtils.toSqlState(e); } @@ -1839,8 +1661,7 @@ public T getObject(String columnLabel, Class type) throws SQLException { @Override public void updateObject(int columnIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { - checkClosed(); - ResultSet.super.updateObject(columnIndex, x, targetSqlType, scaleOrLength); + updateObject(getSchema().columnIndexToName(columnIndex), x, targetSqlType, scaleOrLength); } @Override @@ -1851,8 +1672,7 @@ public void updateObject(String columnLabel, Object x, SQLType targetSqlType, in @Override public void updateObject(int columnIndex, Object x, SQLType targetSqlType) throws SQLException { - checkClosed(); - ResultSet.super.updateObject(columnIndex, x, targetSqlType); + updateObject(getSchema().columnIndexToName(columnIndex), x, targetSqlType); } @Override diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java index 02a215568..843466155 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java @@ -2,8 +2,18 @@ import com.clickhouse.data.ClickHouseDataType; -import java.sql.Types; +import java.sql.Date; +import java.sql.JDBCType; +import java.sql.SQLException; +import java.sql.SQLType; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; +import java.time.temporal.TemporalAccessor; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -12,52 +22,97 @@ public class JdbcUtils { //Define a map to store the mapping between ClickHouse data types and SQL data types - private static final Map CLICKHOUSE_TO_SQL_TYPE_MAP = generateTypeMap(); - private static Map generateTypeMap() { - Map map = new TreeMap<>(); // TreeMap is used to sort the keys in natural order so FixedString will be before String :-) (type match should be more accurate) - map.put(ClickHouseDataType.Int8, Types.TINYINT); - map.put(ClickHouseDataType.UInt8, Types.TINYINT); - map.put(ClickHouseDataType.Int16, Types.SMALLINT); - map.put(ClickHouseDataType.UInt16, Types.SMALLINT); - map.put(ClickHouseDataType.Int32, Types.INTEGER); - map.put(ClickHouseDataType.UInt32, Types.INTEGER); - map.put(ClickHouseDataType.Int64, Types.BIGINT); - map.put(ClickHouseDataType.UInt64, Types.BIGINT); - map.put(ClickHouseDataType.Float32, Types.FLOAT); - map.put(ClickHouseDataType.Float64, Types.DOUBLE); - map.put(ClickHouseDataType.Decimal, Types.DECIMAL); - map.put(ClickHouseDataType.Decimal32, Types.DECIMAL); - map.put(ClickHouseDataType.Decimal64, Types.DECIMAL); - map.put(ClickHouseDataType.Decimal128, Types.DECIMAL); - map.put(ClickHouseDataType.String, Types.VARCHAR); - map.put(ClickHouseDataType.FixedString, Types.CHAR); - map.put(ClickHouseDataType.Enum8, Types.VARCHAR); - map.put(ClickHouseDataType.Enum16, Types.VARCHAR); - map.put(ClickHouseDataType.Date, Types.DATE); - map.put(ClickHouseDataType.Date32, Types.DATE); - map.put(ClickHouseDataType.DateTime, Types.TIMESTAMP_WITH_TIMEZONE); - map.put(ClickHouseDataType.DateTime32, Types.TIMESTAMP_WITH_TIMEZONE); - map.put(ClickHouseDataType.DateTime64, Types.TIMESTAMP_WITH_TIMEZONE); - map.put(ClickHouseDataType.Array, Types.ARRAY); - map.put(ClickHouseDataType.Nested, Types.ARRAY); - map.put(ClickHouseDataType.Map, Types.JAVA_OBJECT); + private static final Map CLICKHOUSE_TO_SQL_TYPE_MAP = generateTypeMap(); + private static Map generateTypeMap() { + Map map = new TreeMap<>(); // TreeMap is used to sort the keys in natural order so FixedString will be before String :-) (type match should be more accurate) + map.put(ClickHouseDataType.Int8, JDBCType.TINYINT); + map.put(ClickHouseDataType.UInt8, JDBCType.TINYINT); + map.put(ClickHouseDataType.Int16, JDBCType.SMALLINT); + map.put(ClickHouseDataType.UInt16, JDBCType.SMALLINT); + map.put(ClickHouseDataType.Int32, JDBCType.INTEGER); + map.put(ClickHouseDataType.UInt32, JDBCType.INTEGER); + map.put(ClickHouseDataType.Int64, JDBCType.BIGINT); + map.put(ClickHouseDataType.UInt64, JDBCType.BIGINT); + map.put(ClickHouseDataType.Float32, JDBCType.FLOAT); + map.put(ClickHouseDataType.Float64, JDBCType.DOUBLE); + map.put(ClickHouseDataType.Decimal, JDBCType.DECIMAL); + map.put(ClickHouseDataType.Decimal32, JDBCType.DECIMAL); + map.put(ClickHouseDataType.Decimal64, JDBCType.DECIMAL); + map.put(ClickHouseDataType.Decimal128, JDBCType.DECIMAL); + map.put(ClickHouseDataType.String, JDBCType.VARCHAR); + map.put(ClickHouseDataType.FixedString, JDBCType.CHAR); + map.put(ClickHouseDataType.Enum8, JDBCType.VARCHAR); + map.put(ClickHouseDataType.Enum16, JDBCType.VARCHAR); + map.put(ClickHouseDataType.Date, JDBCType.DATE); + map.put(ClickHouseDataType.Date32, JDBCType.DATE); + map.put(ClickHouseDataType.DateTime, JDBCType.TIMESTAMP_WITH_TIMEZONE); + map.put(ClickHouseDataType.DateTime32, JDBCType.TIMESTAMP_WITH_TIMEZONE); + map.put(ClickHouseDataType.DateTime64, JDBCType.TIMESTAMP_WITH_TIMEZONE); + map.put(ClickHouseDataType.Array, JDBCType.ARRAY); + map.put(ClickHouseDataType.Nested, JDBCType.ARRAY); + map.put(ClickHouseDataType.Map, JDBCType.JAVA_OBJECT); return map; } - public static int convertToSqlType(ClickHouseDataType clickhouseType) { + private static final Map> SQL_TYPE_TO_CLASS_MAP = generateClassMap(); + private static Map> generateClassMap() { + Map> map = new HashMap<>(); + map.put(JDBCType.CHAR, String.class); + map.put(JDBCType.VARCHAR, String.class); + map.put(JDBCType.LONGVARCHAR, String.class); + map.put(JDBCType.NUMERIC, java.math.BigDecimal.class); + map.put(JDBCType.DECIMAL, java.math.BigDecimal.class); + map.put(JDBCType.BIT, Boolean.class); + map.put(JDBCType.BOOLEAN, Boolean.class); + map.put(JDBCType.TINYINT, Integer.class); + map.put(JDBCType.SMALLINT, Integer.class); + map.put(JDBCType.INTEGER, Integer.class); + map.put(JDBCType.BIGINT, Long.class); + map.put(JDBCType.REAL, Float.class); + map.put(JDBCType.FLOAT, Double.class); + map.put(JDBCType.DOUBLE, Double.class); + map.put(JDBCType.BINARY, byte[].class); + map.put(JDBCType.VARBINARY, byte[].class); + map.put(JDBCType.LONGVARBINARY, byte[].class); + map.put(JDBCType.DATE, Date.class); + map.put(JDBCType.TIME, java.sql.Time.class); + map.put(JDBCType.TIMESTAMP, java.sql.Timestamp.class); + map.put(JDBCType.TIME_WITH_TIMEZONE, java.sql.Time.class); + map.put(JDBCType.TIMESTAMP_WITH_TIMEZONE, java.sql.Timestamp.class); + map.put(JDBCType.CLOB, java.sql.Clob.class); + map.put(JDBCType.BLOB, java.sql.Blob.class); + map.put(JDBCType.ARRAY, java.sql.Array.class); + map.put(JDBCType.STRUCT, java.sql.Struct.class); + map.put(JDBCType.REF, java.sql.Ref.class); + map.put(JDBCType.DATALINK, java.net.URL.class); + map.put(JDBCType.ROWID, java.sql.RowId.class); + map.put(JDBCType.NCHAR, String.class); + map.put(JDBCType.NVARCHAR, String.class); + map.put(JDBCType.LONGNVARCHAR, String.class); + map.put(JDBCType.NCLOB, java.sql.NClob.class); + map.put(JDBCType.SQLXML, java.sql.SQLXML.class); + return map; + } + + public static SQLType convertToSqlType(ClickHouseDataType clickhouseType) { if (clickhouseType == null) { - return Types.NULL; + return JDBCType.NULL; } - return CLICKHOUSE_TO_SQL_TYPE_MAP.getOrDefault(clickhouseType, Types.OTHER); + return CLICKHOUSE_TO_SQL_TYPE_MAP.getOrDefault(clickhouseType, JDBCType.OTHER); } + public static Class convertToJavaClass(ClickHouseDataType clickhouseType) { + return SQL_TYPE_TO_CLASS_MAP.get(convertToSqlType(clickhouseType)); + } + + public static String generateSqlTypeEnum(String columnName) { StringBuilder sql = new StringBuilder("multiIf("); for (ClickHouseDataType type : CLICKHOUSE_TO_SQL_TYPE_MAP.keySet()) { - sql.append("position(").append(columnName).append(", '").append(type.name()).append("') > 0, ").append(CLICKHOUSE_TO_SQL_TYPE_MAP.get(type)).append(", "); + sql.append("position(").append(columnName).append(", '").append(type.name()).append("') > 0, ").append(CLICKHOUSE_TO_SQL_TYPE_MAP.get(type).getVendorTypeNumber()).append(", "); } - sql.append(Types.OTHER + ")"); + sql.append(JDBCType.OTHER.getVendorTypeNumber()).append(")"); return sql.toString(); } @@ -141,4 +196,54 @@ public static String generateSqlTypeSizes(String columnName) { return sql.toString(); } + + public static Object convert(Object value, Class type) throws SQLException { + if (value == null || type == null) { + return value; + } + try { + if (type.isInstance(value)) { + return value; + } else if (type == String.class) { + return value.toString(); + } else if (type == Boolean.class || type == boolean.class) { + return Boolean.parseBoolean(value.toString()); + } else if (type == Byte.class || type == byte.class) { + return Byte.parseByte(value.toString()); + } else if (type == Short.class || type == short.class) { + return Short.parseShort(value.toString()); + } else if (type == Integer.class || type == int.class) { + return Integer.parseInt(value.toString()); + } else if (type == Long.class || type == long.class) { + return Long.parseLong(value.toString()); + } else if (type == Float.class || type == float.class) { + return Float.parseFloat(value.toString()); + } else if (type == Double.class || type == double.class) { + return Double.parseDouble(value.toString()); + } else if (type == java.math.BigDecimal.class) { + return new java.math.BigDecimal(value.toString()); + } else if (type == byte[].class) { + return value.toString().getBytes(); + } else if (type == LocalDate.class && value instanceof TemporalAccessor) { + return LocalDate.from((TemporalAccessor) value); + } else if (type == LocalDateTime.class && value instanceof TemporalAccessor) { + return LocalDateTime.from((TemporalAccessor) value); + } else if (type == OffsetDateTime.class && value instanceof TemporalAccessor) { + return OffsetDateTime.from((TemporalAccessor) value); + } else if (type == ZonedDateTime.class && value instanceof TemporalAccessor) { + return ZonedDateTime.from((TemporalAccessor) value); + } else if (type == Date.class && value instanceof TemporalAccessor) { + return Date.valueOf(LocalDate.from((TemporalAccessor) value)); + } else if (type == java.sql.Timestamp.class && value instanceof TemporalAccessor) { + return java.sql.Timestamp.valueOf(LocalDateTime.from((TemporalAccessor) value)); + } else if (type == java.sql.Time.class && value instanceof TemporalAccessor) { + return java.sql.Time.valueOf(LocalTime.from((TemporalAccessor) value)); + } + } catch (Exception e) { + throw new SQLException("Failed to convert " + value + " to " + type.getName(), ExceptionUtils.SQL_STATE_DATA_EXCEPTION); + } + + throw new SQLException("Unsupported conversion from " + value.getClass().getName() + " to " + type.getName(), ExceptionUtils.SQL_STATE_DATA_EXCEPTION); + } + } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ParameterMetaData.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ParameterMetaData.java index c91a17d42..912aaf501 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ParameterMetaData.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ParameterMetaData.java @@ -76,7 +76,7 @@ public int getScale(int param) throws SQLException { public int getParameterType(int param) throws SQLException { //TODO: Should we implement .getSQLType()? try { - return JdbcUtils.convertToSqlType(getParam(param).getDataType()); + return JdbcUtils.convertToSqlType(getParam(param).getDataType()).getVendorTypeNumber(); } catch (Exception e) { throw ExceptionUtils.toSqlState(e); } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ResultSetMetaData.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ResultSetMetaData.java index a0048c6dd..d5dea3347 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ResultSetMetaData.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ResultSetMetaData.java @@ -133,7 +133,7 @@ public String getCatalogName(int column) throws SQLException { @Override public int getColumnType(int column) throws SQLException { try { - return JdbcUtils.convertToSqlType(getColumn(column).getDataType()); + return JdbcUtils.convertToSqlType(getColumn(column).getDataType()).getVendorTypeNumber(); } catch (Exception e) { throw ExceptionUtils.toSqlState(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 45ac99e99..00b35dcfe 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java @@ -8,14 +8,21 @@ import java.math.BigDecimal; import java.math.BigInteger; -import java.sql.Blob; import java.sql.Connection; import java.sql.Date; import java.sql.DriverManager; +import java.sql.JDBCType; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Random; @@ -838,4 +845,70 @@ public void testDynamicTypesSimpleStatement() throws SQLException { } } } + + + @Test(groups = { "integration" }) + public void testTypeConversions() throws Exception { + try (Connection conn = getConnection()) { + try (Statement stmt = conn.createStatement()) { + try (ResultSet rs = stmt.executeQuery("SELECT 1, 'true', '1.0', " + + "toDate('2024-12-01'), toDateTime('2024-12-01 12:34:56'), toDateTime64('2024-12-01 12:34:56.789', 3), toDateTime64('2024-12-01 12:34:56.789789', 6), toDateTime64('2024-12-01 12:34:56.789789789', 9)")) { + assertTrue(rs.next()); + assertEquals(rs.getInt(1), 1); + assertEquals(String.valueOf(rs.getObject(1)), "1"); + assertEquals(rs.getObject(1, Integer.class), 1); + assertEquals(rs.getObject(1, Long.class), 1L); + assertEquals(String.valueOf(rs.getObject(1, new HashMap>(){{put(JDBCType.INTEGER.getName(), Integer.class);}})), "1"); + + assertTrue(rs.getBoolean(2)); + assertEquals(String.valueOf(rs.getObject(2)), "true"); + assertEquals(rs.getObject(2, Boolean.class), true); + assertEquals(String.valueOf(rs.getObject(2, new HashMap>(){{put(JDBCType.BOOLEAN.getName(), Boolean.class);}})), "true"); + + assertEquals(rs.getFloat(3), 1.0f); + assertEquals(String.valueOf(rs.getObject(3)), "1.0"); + assertEquals(rs.getObject(3, Float.class), 1.0f); + 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), 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-01T00:00Z[UTC]");//Underlying object is ZonedDateTime + assertEquals(rs.getObject(4, LocalDate.class), LocalDate.of(2024, 12, 1)); + 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), Timestamp.valueOf("2024-12-01 12:34:56")); + 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]"); + assertEquals(rs.getObject(5, LocalDateTime.class), LocalDateTime.of(2024, 12, 1, 12, 34, 56)); + 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_WITH_TIMEZONE.getName(), LocalDateTime.class);}})), "2024-12-01T12:34:56"); + + assertEquals(rs.getTimestamp(6), Timestamp.valueOf("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_WITH_TIMEZONE.getName(), LocalDateTime.class);}})), "2024-12-01T12:34:56.789"); + + assertEquals(rs.getTimestamp(7), Timestamp.valueOf("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_WITH_TIMEZONE.getName(), OffsetDateTime.class);}})), "2024-12-01T12:34:56.789789Z"); + + assertEquals(rs.getTimestamp(8), Timestamp.valueOf("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]"); + assertEquals(rs.getObject(8, LocalDateTime.class), LocalDateTime.of(2024, 12, 1, 12, 34, 56, 789789789)); + assertEquals(String.valueOf(rs.getObject(8, new HashMap>(){{put(JDBCType.TIMESTAMP_WITH_TIMEZONE.getName(), ZonedDateTime.class);}})), "2024-12-01T12:34:56.789789789Z[UTC]"); + } + } + } + } }