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(); + } + } +}