This is an automated email from the ASF dual-hosted git repository.

korlov pushed a commit to branch jdbc_over_thin_sql
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/jdbc_over_thin_sql by this 
push:
     new 913e7f9798f IGNITE-26354 Jdbc. ResultSet backed by thin client without 
data accessors (#6530)
913e7f9798f is described below

commit 913e7f9798ff226e049de976ff2ab82809bf056b
Author: Max Zhuravkov <shh...@gmail.com>
AuthorDate: Mon Sep 15 13:53:24 2025 +0300

    IGNITE-26354 Jdbc. ResultSet backed by thin client without data accessors 
(#6530)
---
 .../ignite/internal/jdbc/proto/SqlStateCode.java   |    3 +
 .../ignite/jdbc/ItJdbcErrorsAbstractSelfTest.java  |   11 +-
 .../apache/ignite/internal/jdbc/JdbcResultSet.java |   34 +-
 .../ignite/internal/jdbc2/JdbcResultSet.java       | 1801 ++++++++++++++++++++
 .../ignite/internal/jdbc/ColumnDefinition.java     |    2 +-
 .../internal/jdbc/JdbcResultSetBaseSelfTest.java   |   77 +-
 .../internal/jdbc/JdbcResultSetSelfTest.java       |   15 +
 .../internal/jdbc2/JdbcResultSet2SelfTest.java     |  541 ++++++
 8 files changed, 2441 insertions(+), 43 deletions(-)

diff --git 
a/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/SqlStateCode.java
 
b/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/SqlStateCode.java
index 27aea346318..892a3e6f486 100644
--- 
a/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/SqlStateCode.java
+++ 
b/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/SqlStateCode.java
@@ -40,6 +40,9 @@ public final class SqlStateCode {
     /** IO error during communication. */
     public static final String CONNECTION_FAILURE = "08006";
 
+    /** Invalid argument. */
+    public static final String INVALID_ARGUMENT = "22000";
+
     /** Null value occurred where it wasn't expected to. */
     public static final String NULL_VALUE = "22004";
 
diff --git 
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcErrorsAbstractSelfTest.java
 
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcErrorsAbstractSelfTest.java
index ea3197a62de..6e7b8b54bb1 100644
--- 
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcErrorsAbstractSelfTest.java
+++ 
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcErrorsAbstractSelfTest.java
@@ -26,7 +26,6 @@ import static 
org.apache.ignite.internal.jdbc.proto.SqlStateCode.UNSUPPORTED_OPE
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.fail;
 
-import java.net.URL;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
 import java.sql.Date;
@@ -358,13 +357,9 @@ public abstract class ItJdbcErrorsAbstractSelfTest extends 
AbstractJdbcSelfTest
         }, CONVERSION_FAILED, "Cannot convert to timestamp");
     }
 
-    /**
-     * Test error code for the case when user attempts to get {@link URL} 
value from column whose
-     * value can't be converted to a {@link URL}.
-     */
     @Test
-    public void testInvalidUrlFormat() {
-        checkErrorState(() -> {
+    public void getUrlIsNotSupported() {
+        checkNotSupported(() -> {
             try (PreparedStatement stmt = conn.prepareStatement("SELECT 
'zzz'")) {
                 ResultSet rs = stmt.executeQuery();
 
@@ -372,7 +367,7 @@ public abstract class ItJdbcErrorsAbstractSelfTest extends 
AbstractJdbcSelfTest
 
                 rs.getURL(1);
             }
-        }, CONVERSION_FAILED, "Cannot convert to URL");
+        });
     }
 
     /**
diff --git 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java
index 25efcc7592f..9444e056efe 100644
--- 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java
+++ 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java
@@ -23,7 +23,6 @@ import java.io.InputStream;
 import java.io.Reader;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.ByteBuffer;
 import java.sql.Array;
@@ -1003,7 +1002,7 @@ public class JdbcResultSet implements ResultSet {
     public String getCursorName() throws SQLException {
         ensureNotClosed();
 
-        return null;
+        throw new SQLFeatureNotSupportedException("Cursor name is not 
supported.");
     }
 
     /** {@inheritDoc} */
@@ -1019,10 +1018,7 @@ public class JdbcResultSet implements ResultSet {
     public int findColumn(String colLb) throws SQLException {
         ensureNotClosed();
 
-        Objects.requireNonNull(colLb);
-
-        Integer order = columnOrder().get(colLb.toUpperCase());
-
+        Integer order = colLb != null ? columnOrder().get(colLb.toUpperCase()) 
: null;
         if (order == null) {
             throw new SQLException("Column not found: " + colLb, 
SqlStateCode.PARSING_EXCEPTION);
         }
@@ -1203,7 +1199,7 @@ public class JdbcResultSet implements ResultSet {
     public boolean rowUpdated() throws SQLException {
         ensureNotClosed();
 
-        return false;
+        throw new SQLFeatureNotSupportedException("Updates checks are not 
supported.");
     }
 
     /** {@inheritDoc} */
@@ -1211,7 +1207,7 @@ public class JdbcResultSet implements ResultSet {
     public boolean rowInserted() throws SQLException {
         ensureNotClosed();
 
-        return false;
+        throw new SQLFeatureNotSupportedException("Insert checks are not 
supported.");
     }
 
     /** {@inheritDoc} */
@@ -1219,7 +1215,7 @@ public class JdbcResultSet implements ResultSet {
     public boolean rowDeleted() throws SQLException {
         ensureNotClosed();
 
-        return false;
+        throw new SQLFeatureNotSupportedException("Delete checks are not 
supported.");
     }
 
     /** {@inheritDoc} */
@@ -1797,25 +1793,9 @@ public class JdbcResultSet implements ResultSet {
     /** {@inheritDoc} */
     @Override
     public URL getURL(int colIdx) throws SQLException {
-        Object val = getValue(colIdx);
-
-        if (val == null) {
-            return null;
-        }
-
-        Class<?> cls = val.getClass();
+        ensureNotClosed();
 
-        if (cls == URL.class) {
-            return (URL) val;
-        } else if (cls == String.class) {
-            try {
-                return new URL(val.toString());
-            } catch (MalformedURLException e) {
-                throw new SQLException("Cannot convert to URL: " + val, 
SqlStateCode.CONVERSION_FAILED, e);
-            }
-        } else {
-            throw new SQLException("Cannot convert to URL: " + val, 
SqlStateCode.CONVERSION_FAILED);
-        }
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
new file mode 100644
index 00000000000..32f64f76441
--- /dev/null
+++ 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
@@ -0,0 +1,1801 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.jdbc2;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Date;
+import java.sql.NClob;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Map;
+import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
+import org.apache.ignite.internal.lang.IgniteExceptionMapperUtil;
+import org.apache.ignite.internal.sql.ResultSetMetadataImpl;
+import org.apache.ignite.sql.ResultSetMetadata;
+import org.apache.ignite.sql.SqlRow;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * JDBC ResultSet adapter backed by {@link org.apache.ignite.sql.ResultSet}.
+ */
+public class JdbcResultSet implements ResultSet {
+
+    private static final String UPDATES_ARE_NOT_SUPPORTED = "Updates are not 
supported.";
+
+    private static final String SQL_STRUCTURED_TYPE_ARE_NOT_SUPPORTED = "SQL 
structured type are not supported.";
+
+    private static final String SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED = 
"SQL-specific types are not supported.";
+
+    private static final ResultSetMetadata EMPTY_METADATA = new 
ResultSetMetadataImpl(List.of());
+
+    private final org.apache.ignite.sql.ResultSet<SqlRow> rs;
+
+    private final ResultSetMetadata rsMetadata;
+
+    private final Statement statement;
+
+    private int fetchSize;
+
+    private @Nullable SqlRow currentRow;
+
+    private int currentPosition;
+
+    private boolean closed;
+
+    private boolean wasNull;
+
+    private JdbcResultSetMetadata jdbcMeta;
+
+    /**
+     * Constructor.
+     */
+    public JdbcResultSet(
+            org.apache.ignite.sql.ResultSet<SqlRow> rs,
+            Statement statement
+    ) {
+        this.rs = rs;
+
+        ResultSetMetadata metadata = rs.metadata();
+        this.rsMetadata = metadata != null ? metadata : EMPTY_METADATA;
+
+        this.statement = statement;
+        this.currentRow = null;
+        this.closed = false;
+        this.wasNull = false;
+    }
+
+    @Override
+    public boolean next() throws SQLException {
+        ensureNotClosed();
+
+        try {
+            if (!rs.hasNext()) {
+                currentRow = null;
+                return false;
+            }
+            currentRow = rs.next();
+            currentPosition += 1;
+            return true;
+        } catch (Exception e) {
+            Throwable cause = 
IgniteExceptionMapperUtil.mapToPublicException(e);
+            throw new SQLException(cause.getMessage(), cause);
+        }
+    }
+
+    @Override
+    public void close() throws SQLException {
+        if (closed) {
+            return;
+        }
+        closed = true;
+
+        try {
+            rs.close();
+        } catch (Exception e) {
+            Throwable cause = 
IgniteExceptionMapperUtil.mapToPublicException(e);
+            throw new SQLException(cause.getMessage(), cause);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean wasNull() throws SQLException {
+        ensureNotClosed();
+
+        return wasNull;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getString(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getString(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean getBoolean(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean getBoolean(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public byte getByte(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public byte getByte(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public short getShort(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public short getShort(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getInt(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getInt(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public long getLong(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public long getLong(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public float getFloat(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public float getFloat(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double getDouble(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double getDouble(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public BigDecimal getBigDecimal(int colIdx, int scale) throws SQLException 
{
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public BigDecimal getBigDecimal(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public BigDecimal getBigDecimal(String colLb, int scale) throws 
SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public BigDecimal getBigDecimal(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public byte[] getBytes(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public byte[] getBytes(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Date getDate(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Date getDate(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Date getDate(int colIdx, Calendar cal) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Date getDate(String colLb, Calendar cal) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Time getTime(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Time getTime(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Time getTime(int colIdx, Calendar cal) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Time getTime(String colLb, Calendar cal) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Timestamp getTimestamp(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Timestamp getTimestamp(int colIdx, Calendar cal) throws 
SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        return getTimestamp(colIdx);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Timestamp getTimestamp(String colLb, Calendar cal) throws 
SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Timestamp getTimestamp(String colLb) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InputStream getAsciiStream(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not 
supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InputStream getAsciiStream(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not 
supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InputStream getUnicodeStream(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not 
supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InputStream getUnicodeStream(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not 
supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InputStream getBinaryStream(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Stream are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InputStream getBinaryStream(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not 
supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public SQLWarning getWarnings() throws SQLException {
+        ensureNotClosed();
+
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void clearWarnings() throws SQLException {
+        ensureNotClosed();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getCursorName() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Cursor name is not 
supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ResultSetMetaData getMetaData() throws SQLException {
+        ensureNotClosed();
+
+        return initMetadata();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int findColumn(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        try {
+            int index = rsMetadata.indexOf(colLb);
+            if (index >= 0) {
+                return index + 1;
+            }
+        } catch (Exception ignore) {
+            // ignore
+        }
+
+        throw new SQLException("Column not found: " + colLb, 
SqlStateCode.INVALID_ARGUMENT);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Reader getCharacterStream(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not 
supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Reader getCharacterStream(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not 
supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isBeforeFirst() throws SQLException {
+        ensureNotClosed();
+
+        return currentRow == null && rs.hasNext();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isAfterLast() throws SQLException {
+        ensureNotClosed();
+
+        boolean hasNext = rs.hasNext();
+        // Result set is empty
+        if (currentPosition == 0 && !hasNext) {
+            return false;
+        } else {
+            return currentRow == null && !hasNext;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isFirst() throws SQLException {
+        ensureNotClosed();
+
+        return currentRow != null && currentPosition == 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isLast() throws SQLException {
+        ensureNotClosed();
+
+        return currentRow != null && !rs.hasNext();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void beforeFirst() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void afterLast() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean first() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean last() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getRow() throws SQLException {
+        ensureNotClosed();
+
+        return isAfterLast() ? 0 : currentPosition;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean absolute(int row) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean relative(int rows) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean previous() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setFetchDirection(int direction) throws SQLException {
+        ensureNotClosed();
+
+        if (direction != FETCH_FORWARD) {
+            throw new SQLFeatureNotSupportedException("Only forward direction 
is supported");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getFetchDirection() throws SQLException {
+        ensureNotClosed();
+
+        return FETCH_FORWARD;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setFetchSize(int fetchSize) throws SQLException {
+        ensureNotClosed();
+
+        if (fetchSize <= 0) {
+            throw new SQLException("Fetch size must be greater than zero.");
+        }
+
+        this.fetchSize = fetchSize;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getFetchSize() throws SQLException {
+        ensureNotClosed();
+
+        return fetchSize;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getType() throws SQLException {
+        ensureNotClosed();
+
+        return TYPE_FORWARD_ONLY;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getConcurrency() throws SQLException {
+        ensureNotClosed();
+
+        return CONCUR_READ_ONLY;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean rowUpdated() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean rowInserted() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean rowDeleted() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNull(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNull(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBoolean(int colIdx, boolean x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBoolean(String colLb, boolean x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateByte(int colIdx, byte x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateByte(String colLb, byte x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateShort(int colIdx, short x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateShort(String colLb, short x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateInt(int colIdx, int x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateInt(String colLb, int x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateLong(int colIdx, long x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateLong(String colLb, long x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateFloat(int colIdx, float x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateFloat(String colLb, float x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateDouble(int colIdx, double x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateDouble(String colLb, double x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBigDecimal(int colIdx, BigDecimal x) throws SQLException 
{
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBigDecimal(String colLb, BigDecimal x) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateString(int colIdx, String x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateString(String colLb, String x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBytes(int colIdx, byte[] x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBytes(String colLb, byte[] x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateDate(int colIdx, Date x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateDate(String colLb, Date x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateTime(int colIdx, Time x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateTime(String colLb, Time x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateTimestamp(int colIdx, Timestamp x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateTimestamp(String colLb, Timestamp x) throws SQLException 
{
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateAsciiStream(int colIdx, InputStream x, int len) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateAsciiStream(String colLb, InputStream x, int len) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateAsciiStream(int colIdx, InputStream x, long len) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateAsciiStream(String colLb, InputStream x, long len) 
throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateAsciiStream(int colIdx, InputStream x) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateAsciiStream(String colLb, InputStream x) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBinaryStream(int colIdx, InputStream x, int len) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBinaryStream(int colIdx, InputStream x, long len) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBinaryStream(String colLb, InputStream x, long len) 
throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBinaryStream(int colIdx, InputStream x) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBinaryStream(String colLb, InputStream x, int len) 
throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBinaryStream(String colLb, InputStream x) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateCharacterStream(int colIdx, Reader x, int len) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateCharacterStream(String colLb, Reader reader, int len) 
throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateCharacterStream(int colIdx, Reader x, long len) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateCharacterStream(String colLb, Reader reader, long len) 
throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateCharacterStream(int colIdx, Reader x) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateCharacterStream(String colLb, Reader reader) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateObject(int colIdx, Object x, int scaleOrLen) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateObject(int colIdx, Object x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateObject(String colLb, Object x, int scaleOrLen) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateObject(String colLb, Object x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void insertRow() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateRow() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void deleteRow() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void refreshRow() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Row refreshing is not 
supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void cancelRowUpdates() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Row updates are not 
supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void moveToInsertRow() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void moveToCurrentRow() throws SQLException {
+        ensureNotClosed();
+
+        if (getConcurrency() == CONCUR_READ_ONLY) {
+            throw new SQLException("The result set concurrency is 
CONCUR_READ_ONLY");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Statement getStatement() throws SQLException {
+        ensureNotClosed();
+
+        return statement;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getObject(int colIdx, Map<String, Class<?>> map) throws 
SQLException {
+        throw new 
SQLFeatureNotSupportedException(SQL_STRUCTURED_TYPE_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public <T> T getObject(int colIdx, Class<T> targetCls) throws SQLException 
{
+        ensureNotClosed();
+
+        return (T) getObject0(colIdx, targetCls);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public <T> T getObject(String colLb, Class<T> type) throws SQLException {
+        int colIdx = findColumn(colLb);
+
+        return getObject(colIdx, type);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public Object getObject(int colIdx) throws SQLException {
+        return getValue(colIdx);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public Object getObject(String colLb) throws SQLException {
+        int colIdx = findColumn(colLb);
+
+        return getValue(colIdx);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getObject(String colLb, Map<String, Class<?>> map) throws 
SQLException {
+        throw new 
SQLFeatureNotSupportedException(SQL_STRUCTURED_TYPE_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Ref getRef(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Ref getRef(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Blob getBlob(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Blob getBlob(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Clob getClob(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Clob getClob(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Array getArray(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Array getArray(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public URL getURL(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public URL getURL(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateRef(int colIdx, Ref x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateRef(String colLb, Ref x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBlob(int colIdx, Blob x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBlob(String colLb, Blob x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBlob(int colIdx, InputStream inputStream, long len) 
throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBlob(String colLb, InputStream inputStream, long len) 
throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBlob(int colIdx, InputStream inputStream) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateBlob(String colLb, InputStream inputStream) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateClob(int colIdx, Clob x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateClob(String colLb, Clob x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateClob(int colIdx, Reader reader, long len) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateClob(String colLb, Reader reader, long len) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateClob(int colIdx, Reader reader) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateClob(String colLb, Reader reader) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateArray(int colIdx, Array x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateArray(String colLb, Array x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public RowId getRowId(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public RowId getRowId(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateRowId(int colIdx, RowId x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateRowId(String colLb, RowId x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getHoldability() throws SQLException {
+        ensureNotClosed();
+
+        return HOLD_CURSORS_OVER_COMMIT;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isClosed() throws SQLException {
+        return closed || statement.isClosed();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNString(int colIdx, String val) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNString(String colLb, String val) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNClob(int colIdx, NClob val) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNClob(String colLb, NClob val) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNClob(int colIdx, Reader reader, long len) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNClob(String colLb, Reader reader, long len) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNClob(int colIdx, Reader reader) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNClob(String colLb, Reader reader) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public NClob getNClob(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public NClob getNClob(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public SQLXML getSQLXML(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public SQLXML getSQLXML(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateSQLXML(int colIdx, SQLXML xmlObj) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateSQLXML(String colLb, SQLXML xmlObj) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getNString(int colIdx) throws SQLException {
+        return getString(colIdx);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getNString(String colLb) throws SQLException {
+        return getString(colLb);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Reader getNCharacterStream(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Reader getNCharacterStream(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new 
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNCharacterStream(int colIdx, Reader x, long len) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNCharacterStream(String colLb, Reader reader, long len) 
throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNCharacterStream(int colIdx, Reader x) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void updateNCharacterStream(String colLb, Reader reader) throws 
SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException(UPDATES_ARE_NOT_SUPPORTED);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public <T> T unwrap(Class<T> iface) throws SQLException {
+        if (!isWrapperFor(iface)) {
+            throw new SQLException("Result set is not a wrapper for " + 
iface.getName());
+        }
+
+        return (T) this;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isWrapperFor(Class<?> iface) throws SQLException {
+        return iface != null && iface.isAssignableFrom(JdbcResultSet.class);
+    }
+
+    /**
+     * Gets object field value by index.
+     *
+     * @param colIdx Column index.
+     * @return Object field value.
+     * @throws SQLException In case of error.
+     */
+    @Nullable
+    Object getValue(int colIdx) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        if (colIdx < 1 || colIdx > rsMetadata.columns().size()) {
+            throw new SQLException("Invalid column index: " + colIdx, 
SqlStateCode.INVALID_ARGUMENT);
+        }
+
+        try {
+            assert currentRow != null;
+            Object val = currentRow.value(colIdx - 1);
+
+            wasNull = val == null;
+
+            return val;
+        } catch (Exception e) {
+            Throwable cause = 
IgniteExceptionMapperUtil.mapToPublicException(e);
+            throw new SQLException("Unable to value for column: " + colIdx, 
cause);
+        }
+    }
+
+    /**
+     * Ensures that result set is positioned on a row.
+     *
+     * @throws SQLException If result set is not positioned on a row.
+     */
+    private void ensureHasCurrentRow() throws SQLException {
+        if (currentRow == null) {
+            throw new SQLException("Result set is not positioned on a row.");
+        }
+    }
+
+    /**
+     * Ensures that result set is not closed.
+     *
+     * @throws SQLException If result set is closed.
+     */
+    private void ensureNotClosed() throws SQLException {
+        if (closed) {
+            throw new SQLException("Result set is closed.", 
SqlStateCode.INVALID_CURSOR_STATE);
+        }
+    }
+
+    /**
+     * Get object of given class.
+     *
+     * @param colIdx Column index.
+     * @param targetCls Class representing the Java data type to convert the 
designated column to.
+     * @return Converted object.
+     * @throws SQLException On error.
+     */
+    private @Nullable Object getObject0(int colIdx, Class<?> targetCls) throws 
SQLException {
+        if (targetCls == Boolean.class) {
+            return getBoolean(colIdx);
+        } else if (targetCls == Byte.class) {
+            return getByte(colIdx);
+        } else if (targetCls == Short.class) {
+            return getShort(colIdx);
+        } else if (targetCls == Integer.class) {
+            return getInt(colIdx);
+        } else if (targetCls == Long.class) {
+            return getLong(colIdx);
+        } else if (targetCls == Float.class) {
+            return getFloat(colIdx);
+        } else if (targetCls == Double.class) {
+            return getDouble(colIdx);
+        } else if (targetCls == String.class) {
+            return getString(colIdx);
+        } else if (targetCls == BigDecimal.class) {
+            return getBigDecimal(colIdx);
+        } else if (targetCls == Date.class) {
+            return getDate(colIdx);
+        } else if (targetCls == Time.class) {
+            return getTime(colIdx);
+        } else if (targetCls == Timestamp.class) {
+            return getTimestamp(colIdx);
+        } else if (targetCls == byte[].class) {
+            return getBytes(colIdx);
+        } else {
+            Object val = getValue(colIdx);
+
+            if (val == null) {
+                return null;
+            }
+
+            Class<?> cls = val.getClass();
+
+            if (targetCls.isAssignableFrom(cls)) {
+                return val;
+            } else {
+                throw new SQLException("Cannot convert to " + 
targetCls.getName() + ": " + val, SqlStateCode.CONVERSION_FAILED);
+            }
+        }
+    }
+
+    private JdbcResultSetMetadata initMetadata() {
+        if (jdbcMeta == null) {
+            jdbcMeta = new JdbcResultSetMetadata(rsMetadata);
+        }
+        return jdbcMeta;
+    }
+}
diff --git 
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/ColumnDefinition.java
 
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/ColumnDefinition.java
index 77d09118a96..720c0787237 100644
--- 
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/ColumnDefinition.java
+++ 
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/ColumnDefinition.java
@@ -31,7 +31,7 @@ public final class ColumnDefinition {
     public final int scale;
     public final boolean nullable;
 
-    ColumnDefinition(
+    public ColumnDefinition(
             String label,
             ColumnType type,
             int precision,
diff --git 
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetBaseSelfTest.java
 
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetBaseSelfTest.java
index 0b20789a76c..c9ba5c1a0cc 100644
--- 
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetBaseSelfTest.java
+++ 
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetBaseSelfTest.java
@@ -51,6 +51,8 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
 import org.apache.ignite.sql.ColumnType;
 import org.jetbrains.annotations.Nullable;
@@ -128,6 +130,9 @@ public abstract class JdbcResultSetBaseSelfTest extends 
BaseIgniteAbstractTest {
             expectNotSupported(() -> rs.getSQLXML(1));
             expectNotSupported(() -> rs.getSQLXML("C"));
 
+            expectNotSupported(() -> rs.getURL(1));
+            expectNotSupported(() -> rs.getURL("C"));
+
             expectNotSupported(() -> rs.getObject(1, Map.of()));
             expectNotSupported(() -> rs.getObject("C", Map.of()));
         }
@@ -149,10 +154,14 @@ public abstract class JdbcResultSetBaseSelfTest extends 
BaseIgniteAbstractTest {
     }
 
     @Test
-    public void updateMethodsAreNotSupported() throws Exception {
+    public void notSupportedMethods() throws Exception {
         try (ResultSet rs = createSingleRow(new ColumnDefinition("C", 
ColumnType.STRING, 3, 0, false), "ABC")) {
             assertTrue(rs.next());
 
+            expectNotSupported(rs::rowInserted);
+            expectNotSupported(rs::rowUpdated);
+            expectNotSupported(rs::rowDeleted);
+
             expectNotSupported(() -> rs.updateBoolean(1, true));
             expectNotSupported(() -> rs.updateBoolean("C", true));
 
@@ -273,11 +282,34 @@ public abstract class JdbcResultSetBaseSelfTest extends 
BaseIgniteAbstractTest {
             expectNotSupported(rs::insertRow);
 
             expectNotSupported(rs::moveToInsertRow);
+
+            expectNotSupported(rs::getCursorName);
         }
     }
 
     @Test
     public void navigationMethods() throws SQLException {
+        // Empty result set
+        try (ResultSet rs = createResultSet(null,
+                List.of(new ColumnDefinition("C", ColumnType.STRING, 3, 0, 
false)),
+                List.of())
+        ) {
+            assertFalse(rs.isBeforeFirst());
+            assertFalse(rs.isAfterLast());
+            assertFalse(rs.isFirst());
+            assertFalse(rs.isLast());
+            assertEquals(0, rs.getRow());
+
+            assertFalse(rs.next());
+
+            assertFalse(rs.isBeforeFirst());
+            assertFalse(rs.isAfterLast());
+            assertFalse(rs.isFirst());
+            assertFalse(rs.isLast());
+            assertEquals(0, rs.getRow());
+        }
+
+        // Non empty result set
         try (ResultSet rs = createResultSet(null,
                 List.of(new ColumnDefinition("C", ColumnType.STRING, 3, 0, 
false)),
                 List.of(List.of("A"), List.of("B")))
@@ -337,6 +369,11 @@ public abstract class JdbcResultSetBaseSelfTest extends 
BaseIgniteAbstractTest {
             rs.setFetchDirection(ResultSet.FETCH_FORWARD);
             assertEquals(ResultSet.FETCH_FORWARD, rs.getFetchDirection());
 
+            expectSqlException(() -> 
rs.setFetchDirection(ResultSet.FETCH_UNKNOWN),
+                    "Only forward direction is supported");
+            expectSqlException(() -> 
rs.setFetchDirection(ResultSet.FETCH_REVERSE),
+                    "Only forward direction is supported");
+
             assertEquals(ResultSet.CONCUR_READ_ONLY, rs.getConcurrency());
             assertEquals(ResultSet.TYPE_FORWARD_ONLY, rs.getType());
             assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, 
rs.getHoldability());
@@ -737,16 +774,11 @@ public abstract class JdbcResultSetBaseSelfTest extends 
BaseIgniteAbstractTest {
             expectPositioned(() -> rs.getBytes(1));
             expectPositioned(() -> rs.getBytes("C"));
 
-            expectPositioned(() -> rs.getURL(1));
-            expectPositioned(() -> rs.getURL("C"));
-
             // Do not require positioning
             assertThat(rs.getType(), any(Integer.class));
 
             assertThat(rs.getConcurrency(), any(Integer.class));
 
-            assertNull(rs.getCursorName());
-
             assertThat(rs.getFetchDirection(), any(Integer.class));
 
             assertThat(rs.getFetchSize(), any(Integer.class));
@@ -777,6 +809,37 @@ public abstract class JdbcResultSetBaseSelfTest extends 
BaseIgniteAbstractTest {
         }
     }
 
+    @Test
+    public void findColumn() throws SQLException {
+        List<ColumnDefinition> columns = List.of(
+                // Normalized - converted to uppercase
+                new ColumnDefinition("COLUMN", ColumnType.BOOLEAN, 0, 0, 
false),
+                // Metadata stores ids in unquoted form
+                new ColumnDefinition("column", ColumnType.BOOLEAN, 0, 0, 
false),
+                new ColumnDefinition("Column N", ColumnType.BOOLEAN, 0, 0, 
false),
+                new ColumnDefinition(" ", ColumnType.BOOLEAN, 0, 0, false),
+                new ColumnDefinition(":)", ColumnType.BOOLEAN, 0, 0, false)
+        );
+
+        List<List<Object>> rows = IntStream.range(0, columns.size())
+                .mapToObj(i -> List.of((Object) true))
+                .collect(Collectors.toList());
+
+        try (ResultSet rs = createResultSet(null, columns, rows)) {
+            assertEquals(1, rs.findColumn("COLUMN"));
+            assertEquals(2, rs.findColumn("\"column\""));
+            assertEquals(3, rs.findColumn("\"Column N\""));
+            assertEquals(4, rs.findColumn("\" \""));
+            assertEquals(5, rs.findColumn("\":)\""));
+
+            expectSqlException(() -> rs.findColumn(" COLUMN "), "Column not 
found:  COLUMN ");
+            expectSqlException(() -> rs.findColumn(" column "), "Column not 
found:  column ");
+            expectSqlException(() -> rs.findColumn("x"), "Column not found: 
x");
+            expectSqlException(() -> rs.findColumn(null), "Column not found: 
null");
+            expectSqlException(() -> rs.findColumn(""), "Column not found: ");
+        }
+    }
+
     @Test
     public void close() throws SQLException {
         try (ResultSet rs = createResultSet(null,
@@ -1058,7 +1121,7 @@ public abstract class JdbcResultSetBaseSelfTest extends 
BaseIgniteAbstractTest {
         assertThrows(SQLFeatureNotSupportedException.class, m::call);
     }
 
-    private static void expectSqlException(ResultSetMethod m, String message) {
+    protected static void expectSqlException(ResultSetMethod m, String 
message) {
         SQLException err = assertThrows(SQLException.class, m::call);
         assertThat(err.getMessage(), containsString(message));
     }
diff --git 
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetSelfTest.java
 
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetSelfTest.java
index 773c4359235..fbacdef6b02 100644
--- 
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetSelfTest.java
+++ 
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetSelfTest.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
 import java.util.List;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcColumnMeta;
 import org.jetbrains.annotations.Nullable;
+import org.junit.jupiter.api.Disabled;
 import org.mockito.Mockito;
 
 /**
@@ -61,4 +62,18 @@ public class JdbcResultSetSelfTest extends 
JdbcResultSetBaseSelfTest {
             throw new RuntimeException("Unexpected exception", e);
         }
     }
+
+    // findColumn has bugs in the current JDBC driver it is not worth fixing 
them
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-26140";)
+    @Override
+    public void findColumn() throws SQLException {
+        super.navigationMethods();
+    }
+
+    // isAfterLast has bugs in the current JDBC driver it is not worth fixing 
them
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-26140";)
+    @Override
+    public void navigationMethods() throws SQLException {
+        super.navigationMethods();
+    }
 }
diff --git 
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSet2SelfTest.java
 
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSet2SelfTest.java
new file mode 100644
index 00000000000..c74a57f6dc8
--- /dev/null
+++ 
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSet2SelfTest.java
@@ -0,0 +1,541 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.jdbc2;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.UUID;
+import org.apache.ignite.internal.jdbc.ColumnDefinition;
+import org.apache.ignite.internal.jdbc.JdbcResultSetBaseSelfTest;
+import org.apache.ignite.internal.sql.ColumnMetadataImpl;
+import org.apache.ignite.internal.sql.ColumnMetadataImpl.ColumnOriginImpl;
+import org.apache.ignite.internal.sql.ResultSetMetadataImpl;
+import org.apache.ignite.lang.IgniteException;
+import org.apache.ignite.sql.ColumnMetadata;
+import org.apache.ignite.sql.ColumnType;
+import org.apache.ignite.sql.ResultSetMetadata;
+import org.apache.ignite.sql.SqlRow;
+import org.apache.ignite.table.Tuple;
+import org.jetbrains.annotations.Nullable;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.mockito.Mockito;
+import org.mockito.internal.stubbing.answers.ThrowsException;
+
+/**
+ * Runs JdbcResultSetCompatibilityBaseTest against modern 
org.apache.ignite.internal.jdbc2.JdbcResultSet.
+ */
+public class JdbcResultSet2SelfTest extends JdbcResultSetBaseSelfTest {
+
+    // getXXX are not implemented yet
+    // TODO https://issues.apache.org/jira/browse/IGNITE-26369: numerics
+    // TODO https://issues.apache.org/jira/browse/IGNITE-26379: datetime
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-26140";)
+    @Override
+    @ParameterizedTest
+    @EnumSource(names = {"PERIOD", "DURATION"}, mode = EnumSource.Mode.EXCLUDE)
+    public void wasNullPositional(ColumnType columnType) throws SQLException {
+        super.wasNullPositional(columnType);
+    }
+
+    // getXXX are not implemented yet
+    // TODO https://issues.apache.org/jira/browse/IGNITE-26369: numerics
+    // TODO https://issues.apache.org/jira/browse/IGNITE-26379: datetime
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-26140";)
+    @Override
+    @ParameterizedTest
+    @EnumSource(names = {"PERIOD", "DURATION"}, mode = EnumSource.Mode.EXCLUDE)
+    public void wasNullNamed(ColumnType columnType) throws SQLException {
+        super.wasNullNamed(columnType);
+    }
+
+    // getXXX are not implemented yet
+    // TODO https://issues.apache.org/jira/browse/IGNITE-26369: numerics
+    // TODO https://issues.apache.org/jira/browse/IGNITE-26379: datetime
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-26140";)
+    @Test
+    @Override
+    public void getUnknownColumn() throws SQLException {
+        super.getUnknownColumn();
+    }
+
+    @Test
+    public void unwrap() throws SQLException {
+        try (ResultSet rs = createResultSet(null,
+                List.of(new ColumnDefinition("C", ColumnType.BOOLEAN, 0, 0, 
false)),
+                List.of(List.of(true)))
+        ) {
+            {
+                assertTrue(rs.isWrapperFor(JdbcResultSet.class));
+                JdbcResultSet unwrapped = rs.unwrap(JdbcResultSet.class);
+                assertNotNull(unwrapped);
+            }
+
+            {
+                assertTrue(rs.isWrapperFor(ResultSet.class));
+                ResultSet unwrapped = rs.unwrap(ResultSet.class);
+                assertNotNull(unwrapped);
+            }
+
+            {
+                assertFalse(rs.isWrapperFor(Connection.class));
+                SQLException err = assertThrows(SQLException.class, () -> 
rs.unwrap(Connection.class));
+                assertThat(err.getMessage(), containsString("Result set is not 
a wrapper for " + Connection.class.getName()));
+            }
+        }
+    }
+
+    @Test
+    @Override
+    public void getMetadata() throws SQLException {
+        try (ResultSet rs = createResultSet(null, 
+                List.of(new ColumnDefinition("C", ColumnType.BOOLEAN, 0, 0, 
false)), 
+                List.of(List.of(true)))
+        ) {
+            ResultSetMetaData metaData = rs.getMetaData();
+            assertEquals(1, metaData.getColumnCount());
+        }
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void nextExceptionIsWrapped() {
+        // ClientResultSet hasNext() throws
+        {
+            Statement statement = Mockito.mock(Statement.class);
+
+            org.apache.ignite.sql.ResultSet<SqlRow> igniteRs = 
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
+
+            RuntimeException cause = new RuntimeException("Some error");
+            when(igniteRs.hasNext()).thenThrow(cause);
+
+            ResultSet rs = new JdbcResultSet(igniteRs, statement);
+
+            SQLException err = assertThrows(SQLException.class, rs::next);
+            assertEquals("Some error", err.getMessage());
+            assertInstanceOf(IgniteException.class, err.getCause());
+            assertSame(cause, err.getCause().getCause());
+        }
+
+        // ClientResultSet next() throws
+        {
+            Statement statement = Mockito.mock(Statement.class);
+
+            org.apache.ignite.sql.ResultSet<SqlRow> igniteRs = 
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
+
+            RuntimeException cause = new RuntimeException("Some error");
+            when(igniteRs.hasNext()).thenReturn(true);
+            when(igniteRs.next()).thenThrow(cause);
+
+            ResultSet rs = new JdbcResultSet(igniteRs, statement);
+
+            SQLException err = assertThrows(SQLException.class, rs::next);
+            assertEquals("Some error", err.getMessage());
+            assertInstanceOf(IgniteException.class, err.getCause());
+            assertSame(cause, err.getCause().getCause());
+        }
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void closeClosesResultSet() throws SQLException {
+        Statement statement = Mockito.mock(Statement.class);
+
+        org.apache.ignite.sql.ResultSet<SqlRow> igniteRs = 
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
+        when(igniteRs.metadata()).thenReturn(new 
ResultSetMetadataImpl(List.of()));
+
+        ResultSet rs = new JdbcResultSet(igniteRs, statement);
+
+        rs.close();
+        rs.close();
+
+        verify(igniteRs, times(1)).close();
+        verify(igniteRs, times(1)).metadata();
+        verifyNoMoreInteractions(igniteRs);
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void closeExceptionIsWrapped() {
+        Statement statement = Mockito.mock(Statement.class);
+
+        org.apache.ignite.sql.ResultSet<SqlRow> igniteRs = 
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
+
+        RuntimeException cause = new RuntimeException("Some error");
+        doAnswer(new ThrowsException(cause)).when(igniteRs).close();
+
+        ResultSet rs = new JdbcResultSet(igniteRs, statement);
+
+        SQLException err = assertThrows(SQLException.class, rs::close);
+        assertEquals("Some error", err.getMessage());
+        assertInstanceOf(IgniteException.class, err.getCause());
+        assertSame(cause, err.getCause().getCause());
+    }
+
+    @Test
+    public void getValueExceptionIsWrapped() throws SQLException {
+        Statement statement = Mockito.mock(Statement.class);
+
+        org.apache.ignite.sql.ResultSet<SqlRow> igniteRs = 
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
+        SqlRow row = Mockito.mock(SqlRow.class);
+
+        ColumnMetadataImpl column = new ColumnMetadataImpl("C", 
ColumnType.INT32, 0, 0, false, null);
+
+        when(igniteRs.metadata()).thenReturn(new 
ResultSetMetadataImpl(List.of(column)));
+        when(igniteRs.hasNext()).thenReturn(true);
+        when(igniteRs.next()).thenReturn(row);
+
+        RuntimeException cause = new RuntimeException("Corrupted value");
+        when(row.value(0)).thenThrow(cause);
+
+        JdbcResultSet rs = new JdbcResultSet(igniteRs, statement);
+        assertTrue(rs.next());
+
+        SQLException err = assertThrows(SQLException.class, () -> 
rs.getValue(1));
+        assertEquals("Unable to value for column: 1", err.getMessage());
+        assertInstanceOf(IgniteException.class, err.getCause());
+        assertSame(cause, err.getCause().getCause());
+    }
+
+    @Override
+    protected ResultSet createResultSet(@Nullable ZoneId zoneId, 
List<ColumnDefinition> cols, List<List<Object>> rows) {
+        Statement statement = Mockito.mock(Statement.class);
+
+        return createResultSet(statement, zoneId, cols, rows);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static ResultSet createResultSet(
+            Statement statement,
+            @SuppressWarnings("unused")
+            @Nullable ZoneId zoneId,
+            List<ColumnDefinition> cols,
+            List<List<Object>> rows
+    ) {
+
+        // ResultSet has no metadata
+        if (cols.isEmpty() && rows.isEmpty()) {
+            org.apache.ignite.sql.ResultSet<SqlRow> rs = 
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
+            when(rs.metadata()).thenReturn(null);
+
+            return new JdbcResultSet(rs, statement);
+        }
+
+        List<ColumnMetadata> apiCols = new ArrayList<>();
+        for (ColumnDefinition c : cols) {
+            String schema = c.schema;
+            String table = c.table;
+            String column = c.column != null ? c.column : 
c.label.toUpperCase(Locale.US);
+            boolean nullable = true;
+            ColumnOriginImpl origin = new ColumnOriginImpl(schema, table, 
column);
+            apiCols.add(new ColumnMetadataImpl(c.label, c.type, c.precision, 
c.scale, nullable, origin));
+        }
+
+        ResultSetMetadata apiMeta = new ResultSetMetadataImpl(apiCols);
+
+        return new JdbcResultSet(new ResultSetStub(apiMeta, rows), statement);
+    }
+
+    private static class ResultSetStub implements 
org.apache.ignite.sql.ResultSet<SqlRow> {
+        private final ResultSetMetadata meta;
+        private final Iterator<List<Object>> it;
+        private List<Object> current;
+
+        ResultSetStub(ResultSetMetadata meta, List<List<Object>> rows) {
+            this.meta = Objects.requireNonNull(meta, "meta");
+            this.it = rows.iterator();
+            this.current = null;
+        }
+
+        @Override
+        public ResultSetMetadata metadata() {
+            return meta;
+        }
+
+        @Override
+        public boolean hasRowSet() {
+            return true;
+        }
+
+        @Override
+        public long affectedRows() {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public boolean wasApplied() {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public void close() {
+            // Does nothing, checked separately.
+        }
+
+        @Override
+        public boolean hasNext() {
+            return it.hasNext();
+        }
+
+        @Override
+        public SqlRow next() {
+            if (!it.hasNext()) {
+                throw new NoSuchElementException();
+            }
+            current = it.next();
+            return new UnmodifiableSqlRow(current, meta);
+        }
+    }
+
+    private static class UnmodifiableSqlRow implements SqlRow {
+        private final List<Object> values;
+        private final ResultSetMetadata meta;
+
+        UnmodifiableSqlRow(List<Object> values, ResultSetMetadata meta) {
+            this.values = values;
+            this.meta = meta;
+        }
+
+        @Override
+        public ResultSetMetadata metadata() {
+            return meta;
+        }
+
+        @Override
+        public int columnCount() {
+            return meta.columns().size();
+        }
+
+        @Override
+        public String columnName(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public int columnIndex(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public <T> T valueOrDefault(String columnName, T defaultValue) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public Tuple set(String columnName, Object value) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public <T> T value(String columnName) throws IllegalArgumentException {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public <T> T value(int columnIndex) {
+            return (T) values.get(columnIndex);
+        }
+
+        @Override
+        public boolean booleanValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public boolean booleanValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public byte byteValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public byte byteValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public short shortValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public short shortValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public int intValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public int intValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public long longValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public long longValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public float floatValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public float floatValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public double doubleValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public double doubleValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public BigDecimal decimalValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public BigDecimal decimalValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public String stringValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public String stringValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public byte[] bytesValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public byte[] bytesValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public UUID uuidValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public UUID uuidValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public LocalDate dateValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public LocalDate dateValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public LocalTime timeValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public LocalTime timeValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public LocalDateTime datetimeValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public LocalDateTime datetimeValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public Instant timestampValue(String columnName) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public Instant timestampValue(int columnIndex) {
+            throw new IllegalStateException("Should not be called");
+        }
+
+        @Override
+        public Iterator<Object> iterator() {
+            return values.iterator();
+        }
+    }
+}

Reply via email to