From 798d4d5dc31a2be39ebae92939e5860fb3e2ddb9 Mon Sep 17 00:00:00 2001 From: Sergey Chernov Date: Fri, 8 Aug 2025 11:53:45 -0700 Subject: [PATCH 1/7] added feature manager to simplify handling. Added tests for unsupported methods in result setimpl --- .../com/clickhouse/jdbc/ResultSetImpl.java | 328 ++++++------------ .../jdbc/internal/FeatureManager.java | 20 ++ .../clickhouse/jdbc/ResultSetImplTest.java | 163 +++++++++ 3 files changed, 297 insertions(+), 214 deletions(-) create mode 100644 jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/FeatureManager.java 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 fa6cd26a0..b611b21bf 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -5,6 +5,7 @@ import com.clickhouse.client.api.query.QueryResponse; import com.clickhouse.data.ClickHouseDataType; import com.clickhouse.jdbc.internal.ExceptionUtils; +import com.clickhouse.jdbc.internal.FeatureManager; import com.clickhouse.jdbc.internal.JdbcUtils; import com.clickhouse.jdbc.metadata.ResultSetMetaDataImpl; import org.slf4j.Logger; @@ -26,7 +27,6 @@ import java.sql.ResultSetMetaData; import java.sql.RowId; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLType; import java.sql.SQLWarning; import java.sql.SQLXML; @@ -35,7 +35,6 @@ import java.sql.Timestamp; import java.time.ZonedDateTime; import java.util.Calendar; -import java.util.Collection; import java.util.Map; public class ResultSetImpl implements ResultSet, JdbcV2Wrapper { @@ -48,10 +47,17 @@ public class ResultSetImpl implements ResultSet, JdbcV2Wrapper { private boolean wasNull; private final Calendar defaultCalendar; - public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, ClickHouseBinaryFormatReader reader) { + private final FeatureManager featureManager; + + private static final int AFTER_LAST = -1; + private static final int BEFORE_FIRST = 0; + private int rowPos; + + public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, ClickHouseBinaryFormatReader reader) throws SQLException { this.parentStatement = parentStatement; this.response = response; this.reader = reader; + this.featureManager = new FeatureManager(parentStatement.getConnection().getJdbcConfig()); TableSchema tableMetadata = reader.getSchema(); // Result set contains columns from one database (there is a special table engine 'Merge' to do cross DB queries) @@ -61,16 +67,18 @@ public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, Clic this.closed = false; this.wasNull = false; this.defaultCalendar = parentStatement.connection.defaultCalendar; + this.rowPos = BEFORE_FIRST; } - protected ResultSetImpl(ResultSetImpl resultSet) { + protected ResultSetImpl(ResultSetImpl resultSet) throws SQLException{ this.parentStatement = resultSet.parentStatement; this.response = resultSet.response; this.reader = resultSet.reader; this.metaData = resultSet.metaData; this.closed = false; this.wasNull = false; - this.defaultCalendar = parentStatement.connection.defaultCalendar; + this.defaultCalendar = parentStatement.getConnection().defaultCalendar; + this.featureManager = new FeatureManager(parentStatement.getConnection().getJdbcConfig()); } private void checkClosed() throws SQLException { @@ -97,7 +105,13 @@ public boolean next() throws SQLException { checkClosed(); try { - return reader.next() != null; + Object readerRow = reader.next(); + if (readerRow != null) { + rowPos++; + } else { + rowPos = AFTER_LAST; + } + return readerRow != null; } catch (Exception e) { throw ExceptionUtils.toSqlState(e); } @@ -402,10 +416,7 @@ public Timestamp getTimestamp(String columnLabel) throws SQLException { @Override public InputStream getAsciiStream(String columnLabel) throws SQLException { checkClosed(); - //TODO: Add this to ClickHouseBinaryFormatReader - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("AsciiStream is not yet supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("getAsciiStream"); return null; } @@ -419,10 +430,7 @@ public InputStream getUnicodeStream(String columnLabel) throws SQLException { @Override public InputStream getBinaryStream(String columnLabel) throws SQLException { checkClosed(); - //TODO: implement - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("BinaryStream is not yet supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("getBinaryStream"); return null; } @@ -477,9 +485,7 @@ public int findColumn(String columnLabel) throws SQLException { @Override public Reader getCharacterStream(int columnIndex) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("CharacterStream is not yet supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("getCharacterStream"); return null; } @@ -487,9 +493,7 @@ public Reader getCharacterStream(int columnIndex) throws SQLException { @Override public Reader getCharacterStream(String columnLabel) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("CharacterStream is not yet supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("getCharacterStream"); return null; } @@ -518,59 +522,43 @@ public BigDecimal getBigDecimal(String columnLabel) throws SQLException { @Override public boolean isBeforeFirst() throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("isBeforeFirst is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } - - return false; + return rowPos == BEFORE_FIRST; } @Override public boolean isAfterLast() throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("isAfterLast is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } - - return false; + return rowPos == AFTER_LAST; } @Override public boolean isFirst() throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("isFirst is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } - - return false; + return rowPos == 0; } @Override public boolean isLast() throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("isLast is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } - - return false; + return reader.hasNext(); } @Override public void beforeFirst() throws SQLException { checkClosed(); + featureManager.unsupportedFeatureThrow("beforeFirst"); } @Override public void afterLast() throws SQLException { checkClosed(); + featureManager.unsupportedFeatureThrow("afterLast"); } @Override public boolean first() throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("first is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("first"); return false; } @@ -578,10 +566,7 @@ public boolean first() throws SQLException { @Override public boolean last() throws SQLException { checkClosed(); - - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("last is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("last"); return false; } @@ -589,20 +574,13 @@ public boolean last() throws SQLException { @Override public int getRow() throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("getRow is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } - - return 0; + return rowPos; } @Override public boolean absolute(int row) throws SQLException { checkClosed(); - - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("absolute is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("absolute"); return false; } @@ -610,9 +588,7 @@ public boolean absolute(int row) throws SQLException { @Override public boolean relative(int rows) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("relative is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("relative"); return false; } @@ -620,9 +596,7 @@ public boolean relative(int rows) throws SQLException { @Override public boolean previous() throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("previous is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("previous"); return false; } @@ -636,9 +610,7 @@ public int getFetchDirection() throws SQLException { @Override public void setFetchDirection(int direction) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("setFetchDirection is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("setFetchDirection"); } @Override @@ -667,9 +639,7 @@ public int getConcurrency() throws SQLException { @Override public boolean rowUpdated() throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("rowUpdated"); return false; } @@ -677,9 +647,7 @@ public boolean rowUpdated() throws SQLException { @Override public boolean rowInserted() throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("rowInserted"); return false; } @@ -687,9 +655,7 @@ public boolean rowInserted() throws SQLException { @Override public boolean rowDeleted() throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("rowDeleted"); return false; } @@ -792,188 +758,176 @@ public void updateObject(int columnIndex, Object x) throws SQLException { @Override public void updateNull(String columnLabel) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateNull"); + } @Override public void updateBoolean(String columnLabel, boolean x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateBoolean"); + } @Override public void updateByte(String columnLabel, byte x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateByte"); + } @Override public void updateShort(String columnLabel, short x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateShort"); + } @Override public void updateInt(String columnLabel, int x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateInt"); + } @Override public void updateLong(String columnLabel, long x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateLong"); + } @Override public void updateFloat(String columnLabel, float x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateFloat"); + } @Override public void updateDouble(String columnLabel, double x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateDouble"); + } @Override public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateBigDecimal"); + } @Override public void updateString(String columnLabel, String x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateString"); + } @Override public void updateBytes(String columnLabel, byte[] x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateBytes"); + } @Override public void updateDate(String columnLabel, Date x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateDate"); + } @Override public void updateTime(String columnLabel, Time x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateTime"); + } @Override public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateTimestamp"); + } @Override public void updateAsciiStream(String columnLabel, 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); - } + featureManager.unsupportedFeatureThrow("updateAsciiStream"); + } @Override public void updateBinaryStream(String columnLabel, 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); - } + featureManager.unsupportedFeatureThrow("updateBinaryStream"); + } @Override public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateCharacterStream"); + } @Override public void updateObject(String columnLabel, 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); - } + featureManager.unsupportedFeatureThrow("updateObject"); + } @Override public void updateObject(String columnLabel, Object x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateObject"); + } @Override public void insertRow() throws SQLException { checkClosed(); + featureManager.unsupportedFeatureThrow("insertRow"); } @Override public void updateRow() throws SQLException { checkClosed(); + featureManager.unsupportedFeatureThrow("updateRow"); } @Override public void deleteRow() throws SQLException { checkClosed(); + featureManager.unsupportedFeatureThrow("deleteRow"); } @Override public void refreshRow() throws SQLException { checkClosed(); + featureManager.unsupportedFeatureThrow("refreshRow"); } @Override public void cancelRowUpdates() throws SQLException { checkClosed(); + featureManager.unsupportedFeatureThrow("cancelRowUpdates"); } @Override public void moveToInsertRow() throws SQLException { checkClosed(); + featureManager.unsupportedFeatureThrow("moveToInsertRow"); } @Override public void moveToCurrentRow() throws SQLException { checkClosed(); + featureManager.unsupportedFeatureThrow("moveToCurrentRow"); } @Override @@ -1017,9 +971,7 @@ public Object getObject(String columnLabel, Map> map) throws SQ @Override public Ref getRef(String columnLabel) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Ref is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("getRef"); return null; } @@ -1027,9 +979,7 @@ public Ref getRef(String columnLabel) throws SQLException { @Override public Blob getBlob(String columnLabel) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Blob is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("getBlob"); return null; } @@ -1037,9 +987,7 @@ public Blob getBlob(String columnLabel) throws SQLException { @Override public Clob getClob(String columnLabel) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Clob is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("getClob"); return null; } @@ -1149,9 +1097,7 @@ public void updateRef(int columnIndex, Ref x) throws SQLException { @Override public void updateRef(String columnLabel, Ref x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateRef"); } @Override @@ -1162,9 +1108,7 @@ public void updateBlob(int columnIndex, Blob x) throws SQLException { @Override public void updateBlob(String columnLabel, Blob x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateBlob"); } @Override @@ -1175,9 +1119,7 @@ public void updateClob(int columnIndex, Clob x) throws SQLException { @Override public void updateClob(String columnLabel, Clob x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateClob"); } @Override @@ -1188,9 +1130,7 @@ public void updateArray(int columnIndex, java.sql.Array x) throws SQLException { @Override public void updateArray(String columnLabel, 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); - } + featureManager.unsupportedFeatureThrow("updateArray"); } @Override @@ -1212,9 +1152,7 @@ public void updateRowId(int columnIndex, RowId x) throws SQLException { @Override public void updateRowId(String columnLabel, RowId x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateRowId"); } @Override @@ -1236,9 +1174,7 @@ public void updateNString(int columnIndex, String nString) throws SQLException { @Override public void updateNString(String columnLabel, String nString) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateNString"); } @Override @@ -1249,9 +1185,7 @@ public void updateNClob(int columnIndex, NClob nClob) throws SQLException { @Override public void updateNClob(String columnLabel, NClob nClob) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateNClob"); } @Override @@ -1262,9 +1196,7 @@ public NClob getNClob(int columnIndex) throws SQLException { @Override public NClob getNClob(String columnLabel) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("NClob is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("getNClob"); return null; } @@ -1277,9 +1209,7 @@ public SQLXML getSQLXML(int columnIndex) throws SQLException { @Override public SQLXML getSQLXML(String columnLabel) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("SQLXML is not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("getSQLXML"); return null; } @@ -1292,9 +1222,7 @@ public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException @Override public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateSQLXML"); } @Override @@ -1335,9 +1263,7 @@ public void updateNCharacterStream(int columnIndex, Reader x, long length) throw @Override public void updateNCharacterStream(String columnLabel, 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); - } + featureManager.unsupportedFeatureThrow("updateNCharacterStream"); } @Override @@ -1358,25 +1284,19 @@ public void updateCharacterStream(int columnIndex, Reader x, long length) throws @Override public void updateAsciiStream(String columnLabel, 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); - } + featureManager.unsupportedFeatureThrow("updateAsciiStream"); } @Override public void updateBinaryStream(String columnLabel, 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); - } + featureManager.unsupportedFeatureThrow("updateBinaryStream"); } @Override public void updateCharacterStream(String columnLabel, 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); - } + featureManager.unsupportedFeatureThrow("updateCharacterStream"); } @Override @@ -1387,9 +1307,7 @@ public void updateBlob(int columnIndex, InputStream inputStream, long length) th @Override public void updateBlob(String columnLabel, 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); - } + featureManager.unsupportedFeatureThrow("updateBlob"); } @Override @@ -1400,9 +1318,7 @@ public void updateClob(int columnIndex, Reader reader, long length) throws SQLEx @Override public void updateClob(String columnLabel, 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); - } + featureManager.unsupportedFeatureThrow("updateClob"); } @Override @@ -1413,9 +1329,7 @@ public void updateNClob(int columnIndex, Reader reader, long length) throws SQLE @Override public void updateNClob(String columnLabel, 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); - } + featureManager.unsupportedFeatureThrow("updateNClob"); } @Override @@ -1426,9 +1340,7 @@ public void updateNCharacterStream(int columnIndex, Reader x) throws SQLExceptio @Override public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateNCharacterStream"); } @Override @@ -1449,25 +1361,19 @@ public void updateCharacterStream(int columnIndex, Reader x) throws SQLException @Override public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateAsciiStream"); } @Override public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateBinaryStream"); } @Override public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateCharacterStream"); } @Override @@ -1478,9 +1384,7 @@ public void updateBlob(int columnIndex, InputStream inputStream) throws SQLExcep @Override public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateBlob"); } @Override @@ -1491,9 +1395,7 @@ public void updateClob(int columnIndex, Reader reader) throws SQLException { @Override public void updateClob(String columnLabel, Reader reader) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateClob"); } @Override @@ -1504,9 +1406,7 @@ public void updateNClob(int columnIndex, Reader reader) throws SQLException { @Override public void updateNClob(String columnLabel, Reader reader) throws SQLException { checkClosed(); - if (!parentStatement.connection.config.isIgnoreUnsupportedRequests()) { - throw new SQLFeatureNotSupportedException("Writes are not supported.", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); - } + featureManager.unsupportedFeatureThrow("updateNClob"); } @Override @@ -1560,7 +1460,7 @@ public void updateObject(int columnIndex, Object x, SQLType targetSqlType, int s @Override public void updateObject(String columnLabel, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { checkClosed(); - ResultSet.super.updateObject(columnLabel, x, targetSqlType, scaleOrLength); + featureManager.unsupportedFeatureThrow("updateObject"); } @Override @@ -1571,6 +1471,6 @@ public void updateObject(int columnIndex, Object x, SQLType targetSqlType) throw @Override public void updateObject(String columnLabel, Object x, SQLType targetSqlType) throws SQLException { checkClosed(); - ResultSet.super.updateObject(columnLabel, x, targetSqlType); + featureManager.unsupportedFeatureThrow("updateObject"); } } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/FeatureManager.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/FeatureManager.java new file mode 100644 index 000000000..25670c41e --- /dev/null +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/FeatureManager.java @@ -0,0 +1,20 @@ +package com.clickhouse.jdbc.internal; + +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; + +public class FeatureManager { + + private final JdbcConfiguration configuration; + + public FeatureManager(JdbcConfiguration configuration) { + this.configuration = configuration; + } + + public void unsupportedFeatureThrow(String methodName) throws SQLException { + if (!configuration.isIgnoreUnsupportedRequests()) { + throw new SQLFeatureNotSupportedException(methodName + " is not supported.", + ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); + } + } +} diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java index ebdc94a3b..9bd943aa5 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java @@ -3,11 +3,27 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.math.BigDecimal; +import java.sql.Array; +import java.sql.Blob; import java.sql.Connection; +import java.sql.Date; +import java.sql.JDBCType; +import java.sql.NClob; +import java.sql.Ref; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Properties; +import org.testng.Assert; import org.testng.annotations.Test; public class ResultSetImplTest extends JdbcIntegrationTest { @@ -35,4 +51,151 @@ public void shouldReturnColumnIndex() throws SQLException { } } } + + @Test(groups = { "integration" }) + public void testUnsupportedOperations() throws Throwable { + + boolean[] throwUnsupportedException = new boolean[] {false, true}; + + for (boolean flag : throwUnsupportedException) { + Properties props = new Properties(); + if (flag) { + props.setProperty(DriverProperties.IGNORE_UNSUPPORTED_VALUES.getKey(), "true"); + } + + try (Connection conn = this.getJdbcConnection(props); Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT 1")) { + Assert.ThrowingRunnable[] rsUnsupportedMethods = new Assert.ThrowingRunnable[]{ + () -> rs.first(), + () -> rs.afterLast(), + () -> rs.beforeFirst(), + () -> rs.absolute(-1), + () -> rs.relative(-1), + () -> rs.moveToCurrentRow(), + () -> rs.moveToInsertRow(), + () -> rs.last(), + () -> rs.previous(), + () -> rs.refreshRow(), + () -> rs.updateBoolean("col1", true), + () -> rs.updateByte("col1", (byte) 1), + () -> rs.updateShort("col1", (short) 1), + () -> rs.updateInt("col1", 1), + () -> rs.updateLong("col1", 1L), + () -> rs.updateFloat("col1", 1.1f), + () -> rs.updateDouble("col1", 1.1), + () -> rs.updateBigDecimal("col1", BigDecimal.valueOf(1.1)), + () -> rs.updateString("col1", "test"), + () -> rs.updateNString("col1", "test"), + () -> rs.updateBytes("col1", new byte[1]), + () -> rs.updateDate("col1", Date.valueOf("2020-01-01")), + () -> rs.updateTime("col1", Time.valueOf("12:34:56")), + () -> rs.updateTimestamp("col1", Timestamp.valueOf("2020-01-01 12:34:56.789123")), + () -> rs.updateBlob("col1", (Blob) null), + () -> rs.updateClob("col1", new StringReader("test")), + () -> rs.updateNClob("col1", new StringReader("test")), + + () -> rs.updateBoolean(1, true), + () -> rs.updateByte(1, (byte) 1), + () -> rs.updateShort(1, (short) 1), + () -> rs.updateInt(1, 1), + () -> rs.updateLong(1, 1L), + () -> rs.updateFloat(1, 1.1f), + () -> rs.updateDouble(1, 1.1), + () -> rs.updateBigDecimal(1, BigDecimal.valueOf(1.1)), + () -> rs.updateString(1, "test"), + () -> rs.updateNString(1, "test"), + () -> rs.updateBytes(1, new byte[1]), + () -> rs.updateDate(1, Date.valueOf("2020-01-01")), + () -> rs.updateTime(1, Time.valueOf("12:34:56")), + () -> rs.updateTimestamp(1, Timestamp.valueOf("2020-01-01 12:34:56.789123")), + () -> rs.updateBlob(1, (Blob) null), + () -> rs.updateClob(1, new StringReader("test")), + () -> rs.updateNClob(1, new StringReader("test")), + () -> rs.updateSQLXML(1, null), + () -> rs.updateObject(1, 1), + () -> rs.updateObject("col1", 1), + () -> rs.updateObject(1, "test", Types.INTEGER), + () -> rs.updateObject("col1", "test", Types.INTEGER), + () -> rs.updateObject(1, "test", JDBCType.INTEGER), + () -> rs.updateObject("col1", "test", JDBCType.INTEGER), + () -> rs.updateObject(1, "test", JDBCType.INTEGER, 1), + () -> rs.updateCharacterStream(1, new StringReader("test"), 1), + () -> rs.updateCharacterStream("col1", new StringReader("test")), + () -> rs.updateCharacterStream("col1", new StringReader("test"), 1), + () -> rs.updateCharacterStream(1, new StringReader("test"), 1L), + () -> rs.updateCharacterStream("col1", new StringReader("test"), 1L), + () -> rs.updateCharacterStream(1, new StringReader("test")), + () -> rs.updateCharacterStream("col1", new StringReader("test")), + () -> rs.updateNCharacterStream(1, new StringReader("test"), 1), + () -> rs.updateNCharacterStream("col1", new StringReader("test"), 1), + () -> rs.updateNCharacterStream(1, new StringReader("test"), 1L), + () -> rs.updateNCharacterStream("col1", new StringReader("test"), 1L), + () -> rs.updateNCharacterStream(1, new StringReader("test")), + () -> rs.updateNCharacterStream("col1", new StringReader("test")), + () -> rs.updateBlob(1, (InputStream) null), + () -> rs.updateBlob("col1", (InputStream) null), + () -> rs.updateBlob(1, (InputStream) null, -1), + () -> rs.updateBlob("col1", (InputStream) null, -1), + () -> rs.updateBinaryStream(1, (InputStream) null), + () -> rs.updateBinaryStream("col1", (InputStream) null), + () -> rs.updateBinaryStream(1, (InputStream) null, -1), + () -> rs.updateBinaryStream("col1", (InputStream) null, -1), + () -> rs.updateBinaryStream(1, (InputStream) null, -1L), + () -> rs.updateBinaryStream("col1", (InputStream) null, -1L), + () -> rs.updateAsciiStream(1, (InputStream) null), + () -> rs.updateAsciiStream("col1", (InputStream) null), + () -> rs.updateAsciiStream(1, (InputStream) null, -1), + () -> rs.updateAsciiStream("col1", (InputStream) null, -1), + () -> rs.updateAsciiStream(1, (InputStream) null, -1L), + () -> rs.updateAsciiStream("col1", (InputStream) null, -1L), + () -> rs.updateClob(1, (Reader) null), + () -> rs.updateClob("col1", (Reader) null), + () -> rs.updateClob(1, (Reader) null, -1), + () -> rs.updateClob("col1", (Reader) null, -1), + () -> rs.updateClob(1, (Reader) null, -1L), + () -> rs.updateClob("col1", (Reader) null, -1L), + () -> rs.updateNClob(1, (Reader) null), + () -> rs.updateNClob("col1", (Reader) null), + () -> rs.updateNClob(1, (NClob) null), + () -> rs.updateNClob("col1", (NClob) null), + () -> rs.updateNClob(1, (Reader) null, -1), + () -> rs.updateNClob("col1", (Reader) null, -1), + () -> rs.updateNClob(1, (Reader) null, -1L), + () -> rs.updateNClob("col1", (Reader) null, -1L), + () -> rs.updateRef(1, (Ref) null), + () -> rs.updateRef("col1", (Ref) null), + () -> rs.updateArray(1, (Array) null), + () -> rs.updateArray("col1", (Array) null), + () -> rs.getSQLXML(1), + () -> rs.getSQLXML("col1"), + () -> rs.getBlob(1), + () -> rs.getBlob("col1"), + () -> rs.getClob(1), + () -> rs.getClob("col1"), + () -> rs.getNClob(1), + () -> rs.getNClob("col1"), + () -> rs.getRef(1), + () -> rs.getRef("col1"), + () -> rs.cancelRowUpdates(), + () -> rs.updateNull(1), + () -> rs.updateNull("col1"), + + () -> rs.updateRow(), + () -> rs.insertRow(), + () -> rs.deleteRow(), + () -> rs.rowDeleted(), + () -> rs.rowInserted(), + () -> rs.rowUpdated(), + }; + + for (Assert.ThrowingRunnable op : rsUnsupportedMethods) { + if (!flag) { + Assert.assertThrows(SQLFeatureNotSupportedException.class, op ); + } else { + op.run(); + } + } + } + } + } } From 0604ac5d83431e1529654fbb2625eec40deefe71 Mon Sep 17 00:00:00 2001 From: Sergey Chernov Date: Fri, 8 Aug 2025 13:43:24 -0700 Subject: [PATCH 2/7] added tests for wasNull. Fixed bug in statement with close on complete --- .../com/clickhouse/jdbc/ResultSetImpl.java | 21 ++- .../com/clickhouse/jdbc/StatementImpl.java | 9 +- .../clickhouse/jdbc/ResultSetImplTest.java | 140 +++++++++++++++++- .../com/clickhouse/jdbc/StatementTest.java | 2 +- 4 files changed, 158 insertions(+), 14 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 b611b21bf..cc5701ef4 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -51,8 +51,11 @@ public class ResultSetImpl implements ResultSet, JdbcV2Wrapper { private static final int AFTER_LAST = -1; private static final int BEFORE_FIRST = 0; + private static final int FIRST_ROW = 1; private int rowPos; + private int fetchSize; + public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, ClickHouseBinaryFormatReader reader) throws SQLException { this.parentStatement = parentStatement; this.response = response; @@ -68,6 +71,7 @@ public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, Clic this.wasNull = false; this.defaultCalendar = parentStatement.connection.defaultCalendar; this.rowPos = BEFORE_FIRST; + this.fetchSize = parentStatement.getFetchSize(); } protected ResultSetImpl(ResultSetImpl resultSet) throws SQLException{ @@ -534,13 +538,13 @@ public boolean isAfterLast() throws SQLException { @Override public boolean isFirst() throws SQLException { checkClosed(); - return rowPos == 0; + return rowPos == FIRST_ROW; } @Override public boolean isLast() throws SQLException { checkClosed(); - return reader.hasNext(); + return !reader.hasNext() && rowPos != AFTER_LAST; } @Override @@ -574,7 +578,7 @@ public boolean last() throws SQLException { @Override public int getRow() throws SQLException { checkClosed(); - return rowPos; + return rowPos == AFTER_LAST ? 0 : rowPos; } @Override @@ -610,18 +614,24 @@ public int getFetchDirection() throws SQLException { @Override public void setFetchDirection(int direction) throws SQLException { checkClosed(); - featureManager.unsupportedFeatureThrow("setFetchDirection"); + if (direction != ResultSet.FETCH_FORWARD) { + throw new SQLException("This result set object is of FORWARD ONLY type. Only ResultSet.FETCH_FORWARD is allowed as fetchDirection."); + } } @Override public int getFetchSize() throws SQLException { checkClosed(); - return 0; + return fetchSize; } @Override public void setFetchSize(int rows) throws SQLException { checkClosed(); + if (rows < 0) { + throw new SQLException("Number of rows should be a positive integer"); + } + fetchSize = rows; } @Override @@ -1141,6 +1151,7 @@ public RowId getRowId(int columnIndex) throws SQLException { @Override public RowId getRowId(String columnLabel) throws SQLException { checkClosed(); + featureManager.unsupportedFeatureThrow("getRowId"); return null; } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java index 3e1a64848..b71c36594 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java @@ -44,6 +44,8 @@ public class StatementImpl implements Statement, JdbcV2Wrapper { private int maxFieldSize; private boolean escapeProcessingEnabled; + private int fetchSize = 1; + // settings local to a statement protected QuerySettings localSettings; @@ -129,6 +131,7 @@ protected ResultSetImpl executeQueryImpl(String sql, QuerySettings settings) thr // release before this one completes. if (resultSetAutoClose) { closeCurrentResultSet(); + this.closed = false; // restore state because we are going to create a new result set. } QuerySettings mergedSettings = QuerySettings.merge(settings, new QuerySettings()); @@ -362,12 +365,16 @@ public int getFetchDirection() throws SQLException { @Override public void setFetchSize(int rows) throws SQLException { ensureOpen(); + if (rows < 0) { + throw new SQLException("rows should be greater than 0."); + } + this.fetchSize = rows; } @Override public int getFetchSize() throws SQLException { ensureOpen(); - return 0; + return fetchSize; } @Override diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java index 9bd943aa5..8517db0ab 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java @@ -9,6 +9,7 @@ import java.math.BigDecimal; import java.sql.Array; import java.sql.Blob; +import java.sql.Clob; import java.sql.Connection; import java.sql.Date; import java.sql.JDBCType; @@ -52,10 +53,10 @@ public void shouldReturnColumnIndex() throws SQLException { } } - @Test(groups = { "integration" }) + @Test(groups = {"integration"}) public void testUnsupportedOperations() throws Throwable { - boolean[] throwUnsupportedException = new boolean[] {false, true}; + boolean[] throwUnsupportedException = new boolean[]{false, true}; for (boolean flag : throwUnsupportedException) { Properties props = new Properties(); @@ -64,7 +65,7 @@ public void testUnsupportedOperations() throws Throwable { } try (Connection conn = this.getJdbcConnection(props); Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT 1")) { + ResultSet rs = stmt.executeQuery("SELECT 1")) { Assert.ThrowingRunnable[] rsUnsupportedMethods = new Assert.ThrowingRunnable[]{ () -> rs.first(), () -> rs.afterLast(), @@ -92,8 +93,8 @@ public void testUnsupportedOperations() throws Throwable { () -> rs.updateTimestamp("col1", Timestamp.valueOf("2020-01-01 12:34:56.789123")), () -> rs.updateBlob("col1", (Blob) null), () -> rs.updateClob("col1", new StringReader("test")), - () -> rs.updateNClob("col1", new StringReader("test")), - + () -> rs.updateNClob("col1", new StringReader("test")), + () -> rs.updateBoolean(1, true), () -> rs.updateByte(1, (byte) 1), () -> rs.updateShort(1, (short) 1), @@ -176,10 +177,15 @@ public void testUnsupportedOperations() throws Throwable { () -> rs.getNClob("col1"), () -> rs.getRef(1), () -> rs.getRef("col1"), + () -> rs.getRowId(1), + () -> rs.getRowId("col1"), () -> rs.cancelRowUpdates(), () -> rs.updateNull(1), () -> rs.updateNull("col1"), - + () -> rs.updateRowId(1, null), + () -> rs.updateRowId("col1", null), + () -> rs.updateClob(1, (Clob) null), + () -> rs.updateClob("col1", (Clob) null), () -> rs.updateRow(), () -> rs.insertRow(), () -> rs.deleteRow(), @@ -190,7 +196,7 @@ public void testUnsupportedOperations() throws Throwable { for (Assert.ThrowingRunnable op : rsUnsupportedMethods) { if (!flag) { - Assert.assertThrows(SQLFeatureNotSupportedException.class, op ); + Assert.assertThrows(SQLFeatureNotSupportedException.class, op); } else { op.run(); } @@ -198,4 +204,124 @@ public void testUnsupportedOperations() throws Throwable { } } } + + + @Test(groups = {"integration"}) + public void testCursorPosition() throws SQLException { + try (Connection conn = getJdbcConnection(); Statement stmt = conn.createStatement()) { + try (ResultSet rs = stmt.executeQuery("select number from system.numbers LIMIT 2")) { + Assert.assertTrue(rs.isBeforeFirst()); + Assert.assertFalse(rs.isAfterLast()); + Assert.assertFalse(rs.isFirst()); + Assert.assertFalse(rs.isLast()); + Assert.assertEquals(rs.getRow(), 0); + + rs.next(); + + Assert.assertFalse(rs.isBeforeFirst()); + Assert.assertFalse(rs.isAfterLast()); + Assert.assertTrue(rs.isFirst()); + Assert.assertFalse(rs.isLast()); + Assert.assertEquals(rs.getRow(), 1); + + rs.next(); + + Assert.assertFalse(rs.isBeforeFirst()); + Assert.assertFalse(rs.isAfterLast()); + Assert.assertFalse(rs.isFirst()); + Assert.assertTrue(rs.isLast()); + Assert.assertEquals(rs.getRow(), 2); + + rs.next(); + + Assert.assertFalse(rs.isBeforeFirst()); + Assert.assertTrue(rs.isAfterLast()); + Assert.assertFalse(rs.isFirst()); + Assert.assertFalse(rs.isLast()); + Assert.assertEquals(rs.getRow(), 0); + + } + } + } + + + @Test(groups = {"integration"}) + public void testFetchDirectionsAndSize() throws SQLException { + try (Connection conn = getJdbcConnection(); Statement stmt = conn.createStatement()) { + try (ResultSet rs = stmt.executeQuery("select number from system.numbers LIMIT 2")) { + Assert.assertEquals(rs.getFetchDirection(), ResultSet.FETCH_FORWARD); + Assert.expectThrows(SQLException.class, () -> rs.setFetchDirection(ResultSet.FETCH_REVERSE)); + Assert.expectThrows(SQLException.class, () -> rs.setFetchDirection(ResultSet.FETCH_UNKNOWN)); + rs.setFetchDirection(ResultSet.FETCH_FORWARD); + + Assert.assertEquals(rs.getFetchSize(), 1); + rs.setFetchSize(10); + Assert.assertEquals(rs.getFetchSize(), 10); + Assert.expectThrows(SQLException.class, () -> rs.setFetchSize(-10)); + } + } + } + + @Test(groups = {"integration"}) + public void testConstants() throws SQLException { + try (Connection conn = getJdbcConnection(); Statement stmt = conn.createStatement()) { + try (ResultSet rs = stmt.executeQuery("select number from system.numbers LIMIT 2")) { + Assert.assertEquals(rs.getType(), ResultSet.TYPE_FORWARD_ONLY); + Assert.assertEquals(rs.getConcurrency(), ResultSet.CONCUR_READ_ONLY); + Assert.assertEquals(rs.getHoldability(), ResultSet.HOLD_CURSORS_OVER_COMMIT); + } + } + } + + @Test(groups = {"integration"}) + public void testWasNull() throws SQLException { + try (Connection conn = getJdbcConnection(); Statement stmt = conn.createStatement()) { + final String sql = "select NULL::Nullable(%s) as v1"; + + try (ResultSet rs = stmt.executeQuery(sql.formatted("Int64"))) { + rs.next(); + Assert.assertFalse(rs.wasNull()); + + Assert.assertEquals(rs.getByte(1), (byte) 0); + Assert.assertTrue(rs.wasNull()); + Assert.assertEquals(rs.getByte("v1"), (byte) 0); + Assert.assertTrue(rs.wasNull()); + + Assert.assertEquals(rs.getShort(1), (short) 0); + Assert.assertTrue(rs.wasNull()); + Assert.assertEquals(rs.getShort("v1"), (short) 0); + Assert.assertTrue(rs.wasNull()); + + Assert.assertEquals(rs.getInt(1), 0); + Assert.assertTrue(rs.wasNull()); + Assert.assertEquals(rs.getInt("v1"), 0); + Assert.assertTrue(rs.wasNull()); + + Assert.assertEquals(rs.getLong(1), 0L); + Assert.assertTrue(rs.wasNull()); + Assert.assertEquals(rs.getLong("v1"), 0L); + Assert.assertTrue(rs.wasNull()); + + Assert.assertNull(rs.getBigDecimal(1)); + Assert.assertTrue(rs.wasNull()); + Assert.assertNull(rs.getBigDecimal("v1")); + Assert.assertTrue(rs.wasNull()); + + Assert.assertEquals(rs.getFloat(1), 0f); + Assert.assertTrue(rs.wasNull()); + Assert.assertEquals(rs.getFloat("v1"), 0f); + Assert.assertTrue(rs.wasNull()); + + Assert.assertEquals(rs.getDouble(1), 0d); + Assert.assertTrue(rs.wasNull()); + Assert.assertEquals(rs.getDouble("v1"), 0d); + Assert.assertTrue(rs.wasNull()); + + Assert.assertEquals(rs.getBoolean(1), false); + Assert.assertTrue(rs.wasNull()); + Assert.assertEquals(rs.getBoolean("v1"), false); + Assert.assertTrue(rs.wasNull()); + } + } + } } 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 fa45ab756..520177545 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java @@ -958,7 +958,7 @@ public void testVariousSimpleMethods() throws Exception { stmt.setQueryTimeout(100); Assert.assertEquals(stmt.getQueryTimeout(), 100); stmt.setFetchSize(100); - Assert.assertEquals(stmt.getFetchSize(), 0); // we ignore this hint + Assert.assertEquals(stmt.getFetchSize(), 100); // we ignore this hint Assert.assertEquals(stmt.getResultSetConcurrency(), ResultSet.CONCUR_READ_ONLY); Assert.assertEquals(stmt.getResultSetType(), ResultSet.TYPE_FORWARD_ONLY); Assert.assertNotNull(stmt.getConnection()); From 6d0f5bcba771db13ae2c4061178b19ff61181156 Mon Sep 17 00:00:00 2001 From: Sergey Chernov Date: Fri, 8 Aug 2025 14:01:11 -0700 Subject: [PATCH 3/7] added tests for metadata --- .../com/clickhouse/jdbc/ResultSetImpl.java | 1 + .../clickhouse/jdbc/ResultSetImplTest.java | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) 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 cc5701ef4..a23c4164d 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -453,6 +453,7 @@ public void clearWarnings() throws SQLException { @Override public String getCursorName() throws SQLException { checkClosed(); + featureManager.unsupportedFeatureThrow("getCursorName"); return ""; } diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java index 8517db0ab..7f0fba02c 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java @@ -16,8 +16,10 @@ import java.sql.NClob; import java.sql.Ref; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; +import java.sql.SQLType; import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; @@ -192,6 +194,7 @@ public void testUnsupportedOperations() throws Throwable { () -> rs.rowDeleted(), () -> rs.rowInserted(), () -> rs.rowUpdated(), + () -> rs.getCursorName(), }; for (Assert.ThrowingRunnable op : rsUnsupportedMethods) { @@ -266,6 +269,7 @@ public void testFetchDirectionsAndSize() throws SQLException { public void testConstants() throws SQLException { try (Connection conn = getJdbcConnection(); Statement stmt = conn.createStatement()) { try (ResultSet rs = stmt.executeQuery("select number from system.numbers LIMIT 2")) { + Assert.assertSame(rs.getStatement(), stmt); Assert.assertEquals(rs.getType(), ResultSet.TYPE_FORWARD_ONLY); Assert.assertEquals(rs.getConcurrency(), ResultSet.CONCUR_READ_ONLY); Assert.assertEquals(rs.getHoldability(), ResultSet.HOLD_CURSORS_OVER_COMMIT); @@ -324,4 +328,22 @@ public void testWasNull() throws SQLException { } } } + + @Test(groups = {"integration"}) + public void testGetMetadata() throws SQLException { + try (Connection conn = getJdbcConnection(); Statement stmt = conn.createStatement()) { + try (ResultSet rs = stmt.executeQuery("select '1'::Int32 as v1, 'test' as v2 ")) { + + int v1ColumnIndex = rs.findColumn("v1"); + int v2ColumnIndex = rs.findColumn("v2"); + + ResultSetMetaData metaData = rs.getMetaData(); + Assert.assertEquals(metaData.getColumnCount(), 2); + Assert.assertEquals(metaData.getColumnType(1), Types.INTEGER); + Assert.assertEquals(metaData.getColumnType(2), Types.VARCHAR); + Assert.assertEquals(metaData.getColumnTypeName(1), "Int32"); + Assert.assertEquals(metaData.getColumnTypeName(2), "String"); + } + } + } } From b223c4cb32281059303699b970c750c73576b286 Mon Sep 17 00:00:00 2001 From: Sergey Chernov Date: Fri, 8 Aug 2025 15:19:04 -0700 Subject: [PATCH 4/7] fixed a few sonar issues --- .../clickhouse/jdbc/ResultSetImplTest.java | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java index 7f0fba02c..2adfcf220 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java @@ -1,7 +1,7 @@ package com.clickhouse.jdbc; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; +import org.testng.Assert; +import org.testng.annotations.Test; import java.io.InputStream; import java.io.Reader; @@ -19,15 +19,14 @@ import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; -import java.sql.SQLType; import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.Properties; -import org.testng.Assert; -import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; public class ResultSetImplTest extends JdbcIntegrationTest { @@ -69,16 +68,16 @@ public void testUnsupportedOperations() throws Throwable { try (Connection conn = this.getJdbcConnection(props); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT 1")) { Assert.ThrowingRunnable[] rsUnsupportedMethods = new Assert.ThrowingRunnable[]{ - () -> rs.first(), - () -> rs.afterLast(), - () -> rs.beforeFirst(), + rs::first, + rs::afterLast, + rs::beforeFirst, () -> rs.absolute(-1), () -> rs.relative(-1), - () -> rs.moveToCurrentRow(), - () -> rs.moveToInsertRow(), - () -> rs.last(), - () -> rs.previous(), - () -> rs.refreshRow(), + rs::moveToCurrentRow, + rs::moveToInsertRow, + rs::last, + rs::previous, + rs::refreshRow, () -> rs.updateBoolean("col1", true), () -> rs.updateByte("col1", (byte) 1), () -> rs.updateShort("col1", (short) 1), @@ -181,20 +180,20 @@ public void testUnsupportedOperations() throws Throwable { () -> rs.getRef("col1"), () -> rs.getRowId(1), () -> rs.getRowId("col1"), - () -> rs.cancelRowUpdates(), + rs::cancelRowUpdates, () -> rs.updateNull(1), () -> rs.updateNull("col1"), () -> rs.updateRowId(1, null), () -> rs.updateRowId("col1", null), () -> rs.updateClob(1, (Clob) null), () -> rs.updateClob("col1", (Clob) null), - () -> rs.updateRow(), - () -> rs.insertRow(), - () -> rs.deleteRow(), - () -> rs.rowDeleted(), - () -> rs.rowInserted(), - () -> rs.rowUpdated(), - () -> rs.getCursorName(), + rs::updateRow, + rs::insertRow, + rs::deleteRow, + rs::rowDeleted, + rs::rowInserted, + rs::rowUpdated, + rs::getCursorName, }; for (Assert.ThrowingRunnable op : rsUnsupportedMethods) { @@ -339,10 +338,10 @@ public void testGetMetadata() throws SQLException { ResultSetMetaData metaData = rs.getMetaData(); Assert.assertEquals(metaData.getColumnCount(), 2); - Assert.assertEquals(metaData.getColumnType(1), Types.INTEGER); - Assert.assertEquals(metaData.getColumnType(2), Types.VARCHAR); - Assert.assertEquals(metaData.getColumnTypeName(1), "Int32"); - Assert.assertEquals(metaData.getColumnTypeName(2), "String"); + Assert.assertEquals(metaData.getColumnType(v1ColumnIndex), Types.INTEGER); + Assert.assertEquals(metaData.getColumnType(v2ColumnIndex), Types.VARCHAR); + Assert.assertEquals(metaData.getColumnTypeName(v1ColumnIndex), "Int32"); + Assert.assertEquals(metaData.getColumnTypeName(v1ColumnIndex), "String"); } } } From 33953f9625d39faf702b61f95c4994d30303e05b Mon Sep 17 00:00:00 2001 From: Sergey Chernov Date: Fri, 8 Aug 2025 15:26:47 -0700 Subject: [PATCH 5/7] fixed get calender inconsistency. added more words about why to reopen statement --- .../src/main/java/com/clickhouse/jdbc/ResultSetImpl.java | 2 +- .../src/main/java/com/clickhouse/jdbc/StatementImpl.java | 6 +++++- 2 files changed, 6 insertions(+), 2 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 a23c4164d..944b43801 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -69,7 +69,7 @@ public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, Clic JdbcUtils.DATA_TYPE_CLASS_MAP); this.closed = false; this.wasNull = false; - this.defaultCalendar = parentStatement.connection.defaultCalendar; + this.defaultCalendar = parentStatement.getConnection().defaultCalendar; this.rowPos = BEFORE_FIRST; this.fetchSize = parentStatement.getFetchSize(); } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java index b71c36594..cbe687809 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java @@ -131,7 +131,11 @@ protected ResultSetImpl executeQueryImpl(String sql, QuerySettings settings) thr // release before this one completes. if (resultSetAutoClose) { closeCurrentResultSet(); - this.closed = false; // restore state because we are going to create a new result set. + // There is a feature `closeOnComplete` that dictate closing statement when all + // result sets are closed. Call to `closeCurrentResultSet` will trigger this statement + // closure. But it should not happen because this was introduces instead of spec and will be remove in future/ + // So we need make this statement open again because we going to create a new result set. + this.closed = false; } QuerySettings mergedSettings = QuerySettings.merge(settings, new QuerySettings()); From 734f1c6e9fa7826f0dc527826cff606cb81295dd Mon Sep 17 00:00:00 2001 From: Sergey Chernov Date: Mon, 11 Aug 2025 09:17:11 -0700 Subject: [PATCH 6/7] Fixed the test --- .../src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java index 2adfcf220..bb3c8f3b2 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java @@ -341,7 +341,7 @@ public void testGetMetadata() throws SQLException { Assert.assertEquals(metaData.getColumnType(v1ColumnIndex), Types.INTEGER); Assert.assertEquals(metaData.getColumnType(v2ColumnIndex), Types.VARCHAR); Assert.assertEquals(metaData.getColumnTypeName(v1ColumnIndex), "Int32"); - Assert.assertEquals(metaData.getColumnTypeName(v1ColumnIndex), "String"); + Assert.assertEquals(metaData.getColumnTypeName(v2ColumnIndex), "String"); } } } From b4ff2e7508f01e0ffe2b2b156c85c23ed43547e0 Mon Sep 17 00:00:00 2001 From: Sergey Chernov Date: Mon, 18 Aug 2025 14:10:40 -0700 Subject: [PATCH 7/7] fixed isLast for an empty set --- .../java/com/clickhouse/jdbc/ResultSetImpl.java | 2 +- .../java/com/clickhouse/jdbc/StatementImpl.java | 8 ++++---- .../com/clickhouse/jdbc/ResultSetImplTest.java | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 5 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 944b43801..c4647da35 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -545,7 +545,7 @@ public boolean isFirst() throws SQLException { @Override public boolean isLast() throws SQLException { checkClosed(); - return !reader.hasNext() && rowPos != AFTER_LAST; + return !reader.hasNext() && rowPos != AFTER_LAST && rowPos != BEFORE_FIRST; } @Override diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java index cbe687809..a3e10c53b 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java @@ -131,10 +131,10 @@ protected ResultSetImpl executeQueryImpl(String sql, QuerySettings settings) thr // release before this one completes. if (resultSetAutoClose) { closeCurrentResultSet(); - // There is a feature `closeOnComplete` that dictate closing statement when all + // There is a feature `closeOnComplete` that dictates closing the statement when all // result sets are closed. Call to `closeCurrentResultSet` will trigger this statement - // closure. But it should not happen because this was introduces instead of spec and will be remove in future/ - // So we need make this statement open again because we going to create a new result set. + // closure. But it should not happen because this was introduces instead of spec and will be removed in the future. + // So we need to make this statement open again because we're going to create a new result set. this.closed = false; } @@ -370,7 +370,7 @@ public int getFetchDirection() throws SQLException { public void setFetchSize(int rows) throws SQLException { ensureOpen(); if (rows < 0) { - throw new SQLException("rows should be greater than 0."); + throw new SQLException("rows should be greater or equal to 0."); } this.fetchSize = rows; } diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java index bb3c8f3b2..a97e4d492 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ResultSetImplTest.java @@ -241,7 +241,22 @@ public void testCursorPosition() throws SQLException { Assert.assertFalse(rs.isFirst()); Assert.assertFalse(rs.isLast()); Assert.assertEquals(rs.getRow(), 0); + } + try (ResultSet rs = stmt.executeQuery("select 1 LIMIT 0")) { + Assert.assertTrue(rs.isBeforeFirst()); + Assert.assertFalse(rs.isAfterLast()); + Assert.assertFalse(rs.isFirst()); + Assert.assertFalse(rs.isLast()); + Assert.assertEquals(rs.getRow(), 0); + + Assert.assertFalse(rs.next()); + + Assert.assertFalse(rs.isBeforeFirst()); // we stepped over the end + Assert.assertTrue(rs.isAfterLast()); // we stepped over the end + Assert.assertFalse(rs.isFirst()); + Assert.assertFalse(rs.isLast()); + Assert.assertEquals(rs.getRow(), 0); } } }