This is an automated email from the ASF dual-hosted git repository.
ppa 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 74861d43904 IGNITE-26428 Jdbc. PreparedStatement for thin client
backed connection (#6677)
74861d43904 is described below
commit 74861d439045a0bd3f1d6ce45a750a941ab6afbf
Author: Max Zhuravkov <[email protected]>
AuthorDate: Thu Oct 9 17:30:44 2025 +0300
IGNITE-26428 Jdbc. PreparedStatement for thin client backed connection
(#6677)
---
.../internal/jdbc2/JdbcPreparedStatement2.java | 830 +++++++++++++++++++++
.../ignite/internal/jdbc2/JdbcStatement2.java | 30 +-
.../jdbc2/JdbcPreparedStatement2SelfTest.java | 727 ++++++++++++++++++
.../internal/jdbc2/JdbcStatement2SelfTest.java | 6 +-
4 files changed, 1575 insertions(+), 18 deletions(-)
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement2.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement2.java
new file mode 100644
index 00000000000..4e74ebd579f
--- /dev/null
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement2.java
@@ -0,0 +1,830 @@
+/*
+ * 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.Connection;
+import java.sql.Date;
+import java.sql.JDBCType;
+import java.sql.NClob;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
+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.SQLType;
+import java.sql.SQLXML;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
+import org.apache.ignite.sql.IgniteSql;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Jdbc prepared statement.
+ */
+public class JdbcPreparedStatement2 extends JdbcStatement2 implements
PreparedStatement {
+
+ /** Supported JDBC types. */
+ private static final Set<JDBCType> SUPPORTED_TYPES = EnumSet.of(
+ JDBCType.BOOLEAN,
+ JDBCType.TINYINT,
+ JDBCType.SMALLINT,
+ JDBCType.INTEGER,
+ JDBCType.BIGINT,
+ JDBCType.FLOAT,
+ JDBCType.REAL,
+ JDBCType.DOUBLE,
+ JDBCType.DECIMAL,
+ JDBCType.DATE,
+ JDBCType.TIME,
+ JDBCType.TIMESTAMP,
+ JDBCType.CHAR,
+ JDBCType.VARCHAR,
+ JDBCType.BINARY,
+ JDBCType.VARBINARY,
+ JDBCType.NULL,
+ JDBCType.OTHER // UUID.
+ );
+
+ private static final String SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED =
+ "SQL-specific types are not supported.";
+
+ private static final String STREAMS_ARE_NOT_SUPPORTED =
+ "Streams are not supported.";
+
+ private static final String CONVERSION_TO_TARGET_SQL_TYPE_IS_NOT_SUPPORTED
=
+ "Conversion to target sql type is not supported.";
+
+ private static final String PARAMETER_METADATA_IS_NOT_SUPPORTED =
+ "Parameter metadata is not supported.";
+
+ private static final String RESULT_SET_METADATA_IS_NOT_SUPPORTED =
+ "ResultSet metadata for prepared statement is not supported.";
+
+ private final String sql;
+
+ private List<Object> currentArguments = List.of();
+
+ JdbcPreparedStatement2(
+ Connection connection,
+ IgniteSql igniteSql,
+ String schema,
+ int resHoldability,
+ String sql
+ ) {
+ super(connection, igniteSql, schema, resHoldability);
+
+ this.sql = sql;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResultSet executeQuery() throws SQLException {
+ execute0(QUERY, sql, currentArguments.toArray());
+
+ ResultSet rs = getResultSet();
+
+ if (rs == null) {
+ throw new SQLException("The query isn't SELECT query: " + sql,
SqlStateCode.PARSING_EXCEPTION);
+ }
+
+ return rs;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResultSet executeQuery(String sql) throws SQLException {
+ throw new SQLException("The method 'executeQuery(String)' is called on
PreparedStatement instance.",
+ SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int[] executeBatch() throws SQLException {
+ ensureNotClosed();
+
+ throw new UnsupportedOperationException("Batch operation");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate() throws SQLException {
+ execute0(DML_OR_DDL, sql, currentArguments.toArray());
+
+ int res = getUpdateCount();
+
+ if (res == -1) {
+ throw new SQLException("The query is not DML statement: " + sql,
SqlStateCode.PARSING_EXCEPTION);
+ }
+
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate(String sql) throws SQLException {
+ throw new SQLException("The method 'executeUpdate(String)' is called
on PreparedStatement instance.",
+ SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate(String sql, int autoGeneratedKeys) throws
SQLException {
+ throw new SQLException("The method 'executeUpdate(String, int)' is
called on PreparedStatement instance.",
+ SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate(String sql, String[] colNames) throws
SQLException {
+ throw new SQLException("The method 'executeUpdate(String, String[])'
is called on PreparedStatement "
+ + "instance.", SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate(String sql, int[] colNames) throws SQLException {
+ throw new SQLException("The method 'executeUpdate(String, int[])' is
called on PreparedStatement instance.",
+ SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute() throws SQLException {
+ execute0(ALL, sql, currentArguments.toArray());
+
+ return isQuery();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute(String sql) throws SQLException {
+ throw new SQLException("The method 'execute(String)' is called on
PreparedStatement instance.",
+ SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute(String sql, int autoGeneratedKeys) throws
SQLException {
+ throw new SQLException("The method 'execute(String, int)' is called on
PreparedStatement "
+ + "instance.", SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute(String sql, int[] colNames) throws SQLException {
+ throw new SQLException("The method 'execute(String, int[])' is called
on PreparedStatement "
+ + "instance.", SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute(String sql, String[] colNames) throws SQLException {
+ throw new SQLException("The method 'execute(String, String[]) is
called on PreparedStatement "
+ + "instance.", SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addBatch() throws SQLException {
+ ensureNotClosed();
+
+ throw new UnsupportedOperationException("Batch operation");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addBatch(String sql) throws SQLException {
+ throw new SQLException("The method 'addBatch(String)' is called on
PreparedStatement instance.",
+ SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearBatch() throws SQLException {
+ ensureNotClosed();
+
+ throw new UnsupportedOperationException("Batch operation");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNull(int paramIdx, int sqlType) throws SQLException {
+ ensureNotClosed();
+ checkType(sqlType);
+
+ setArgumentValue(paramIdx, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNull(int paramIdx, int sqlType, String typeName) throws
SQLException {
+ ensureNotClosed();
+ checkType(sqlType);
+
+ setArgumentValue(paramIdx, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBoolean(int paramIdx, boolean x) throws SQLException {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setByte(int paramIdx, byte x) throws SQLException {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setShort(int paramIdx, short x) throws SQLException {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setInt(int paramIdx, int x) throws SQLException {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setLong(int paramIdx, long x) throws SQLException {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setFloat(int paramIdx, float x) throws SQLException {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setDouble(int paramIdx, double x) throws SQLException {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBigDecimal(int paramIdx, BigDecimal x) throws SQLException {
+ if (x == null) {
+ setNull(paramIdx, Types.DECIMAL);
+ } else {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setString(int paramIdx, String x) throws SQLException {
+ if (x == null) {
+ setNull(paramIdx, Types.VARCHAR);
+ } else {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBytes(int paramIdx, byte[] x) throws SQLException {
+ if (x == null) {
+ setNull(paramIdx, Types.VARBINARY);
+ } else {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setDate(int paramIdx, Date x) throws SQLException {
+ if (x == null) {
+ setNull(paramIdx, Types.DATE);
+ } else {
+ setLocalDate(paramIdx, x.toLocalDate());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setDate(int paramIdx, Date x, Calendar cal) throws
SQLException {
+ setDate(paramIdx, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setTime(int paramIdx, Time x) throws SQLException {
+ if (x == null) {
+ setNull(paramIdx, Types.TIME);
+ } else {
+ setLocalTime(paramIdx, x.toLocalTime());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setTime(int paramIdx, Time x, Calendar cal) throws
SQLException {
+ setTime(paramIdx, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setTimestamp(int paramIdx, Timestamp x) throws SQLException {
+ if (x == null) {
+ setNull(paramIdx, Types.TIMESTAMP);
+ } else {
+ setLocalDateTime(paramIdx, x.toLocalDateTime());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setTimestamp(int paramIdx, Timestamp x, Calendar cal) throws
SQLException {
+ setTimestamp(paramIdx, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setAsciiStream(int paramIdx, InputStream x, int len) throws
SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException(STREAMS_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setAsciiStream(int paramIdx, InputStream x) throws
SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException(STREAMS_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setAsciiStream(int paramIdx, InputStream x, long len) throws
SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException(STREAMS_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setUnicodeStream(int paramIdx, InputStream x, int len) throws
SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException(STREAMS_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBinaryStream(int paramIdx, InputStream x, int len) throws
SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException(STREAMS_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBinaryStream(int paramIdx, InputStream x, long len) throws
SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException(STREAMS_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBinaryStream(int paramIdx, InputStream x) throws
SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException(STREAMS_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearParameters() throws SQLException {
+ ensureNotClosed();
+
+ currentArguments = List.of();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setObject(int paramIdx, Object x) throws SQLException {
+ if (x == null) {
+ setNull(paramIdx, Types.NULL);
+ } else if (x instanceof Boolean) {
+ setBoolean(paramIdx, (Boolean) x);
+ } else if (x instanceof Byte) {
+ setByte(paramIdx, (Byte) x);
+ } else if (x instanceof Short) {
+ setShort(paramIdx, (Short) x);
+ } else if (x instanceof Integer) {
+ setInt(paramIdx, (Integer) x);
+ } else if (x instanceof Long) {
+ setLong(paramIdx, (Long) x);
+ } else if (x instanceof Float) {
+ setFloat(paramIdx, (Float) x);
+ } else if (x instanceof Double) {
+ setDouble(paramIdx, (Double) x);
+ } else if (x instanceof BigDecimal) {
+ setBigDecimal(paramIdx, (BigDecimal) x);
+ } else if (x instanceof String) {
+ setString(paramIdx, (String) x);
+ } else if (x instanceof byte[]) {
+ setBytes(paramIdx, (byte[]) x);
+ } else if (x instanceof Date) {
+ setDate(paramIdx, (Date) x);
+ } else if (x instanceof Time) {
+ setTime(paramIdx, (Time) x);
+ } else if (x instanceof Timestamp) {
+ setTimestamp(paramIdx, (Timestamp) x);
+ } else if (x instanceof LocalTime) {
+ setLocalTime(paramIdx, (LocalTime) x);
+ } else if (x instanceof LocalDate) {
+ setLocalDate(paramIdx, (LocalDate) x);
+ } else if (x instanceof LocalDateTime) {
+ setLocalDateTime(paramIdx, (LocalDateTime) x);
+ } else if (x instanceof Instant) {
+ setInstant(paramIdx, (Instant) x);
+ } else if (x instanceof UUID) {
+ setUuid(paramIdx, (UUID) x);
+ } else {
+ throw new SQLException("Parameter type is not supported: " +
x.getClass().getName());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setObject(int paramIdx, Object x, int targetSqlType, int
scaleOrLen) throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(CONVERSION_TO_TARGET_SQL_TYPE_IS_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setObject(int paramIdx, Object x, int targetSqlType) throws
SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(CONVERSION_TO_TARGET_SQL_TYPE_IS_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setObject(int paramIdx, Object x, SQLType targetSqlType)
throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(CONVERSION_TO_TARGET_SQL_TYPE_IS_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setObject(int paramIdx, Object x, SQLType targetSqlType, int
scaleOrLength) throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(CONVERSION_TO_TARGET_SQL_TYPE_IS_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setCharacterStream(int paramIdx, Reader x, int len) throws
SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setCharacterStream(int paramIdx, Reader x, long len) throws
SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setCharacterStream(int paramIdx, Reader x) throws SQLException
{
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setRef(int paramIdx, Ref x) throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBlob(int paramIdx, Blob x) throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBlob(int paramIdx, InputStream inputStream) throws
SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBlob(int paramIdx, InputStream inputStream, long len)
throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setClob(int paramIdx, Clob x) throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setClob(int paramIdx, Reader reader, long len) throws
SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setClob(int paramIdx, Reader reader) throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setArray(int paramIdx, Array x) throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResultSetMetaData getMetaData() throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(RESULT_SET_METADATA_IS_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setURL(int paramIdx, URL x) throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ParameterMetaData getParameterMetaData() throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(PARAMETER_METADATA_IS_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setRowId(int paramIdx, RowId x) throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNString(int paramIdx, String val) throws SQLException {
+ ensureNotClosed();
+
+ setString(paramIdx, val);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNCharacterStream(int paramIdx, Reader val, long len) throws
SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException(STREAMS_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNCharacterStream(int paramIdx, Reader val) throws
SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException(STREAMS_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNClob(int paramIdx, NClob val) throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNClob(int paramIdx, Reader reader) throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNClob(int paramIdx, Reader reader, long len) throws
SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSQLXML(int paramIdx, SQLXML xmlObj) throws SQLException {
+ ensureNotClosed();
+
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public long[] executeLargeBatch() throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("executeLargeBatch not
implemented.");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public long executeLargeUpdate() throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("executeLargeUpdate not
implemented.");
+ }
+
+ @Override
+ public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws
SQLException {
+ ensureNotClosed();
+
+ throw new SQLException("The method 'executeLargeUpdate(String, int)'
is called on PreparedStatement instance.",
+ SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ @Override
+ public long executeLargeUpdate(String sql) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLException("The method 'executeLargeUpdate(String)' is
called on PreparedStatement instance.",
+ SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ @Override
+ public long executeLargeUpdate(String sql, int[] columnIndexes) throws
SQLException {
+ ensureNotClosed();
+
+ throw new SQLException("The method 'executeLargeUpdate(String, int[])'
is called on PreparedStatement instance.",
+ SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ @Override
+ public long executeLargeUpdate(String sql, String[] columnNames) throws
SQLException {
+ ensureNotClosed();
+
+ throw new SQLException("The method 'executeLargeUpdate(String,
String[])' is called on PreparedStatement instance.",
+ SqlStateCode.UNSUPPORTED_OPERATION);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException {
+ if (!isWrapperFor(Objects.requireNonNull(iface))) {
+ throw new SQLException("Prepared statement is not a wrapper for "
+ iface.getName());
+ }
+
+ return (T) this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException {
+ return iface != null &&
iface.isAssignableFrom(JdbcPreparedStatement2.class);
+ }
+
+ private void setLocalDate(int paramIdx, LocalDate x) throws SQLException {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+
+ private void setLocalTime(int paramIdx, LocalTime x) throws SQLException {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+
+ private void setLocalDateTime(int paramIdx, LocalDateTime x) throws
SQLException {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+
+ private void setInstant(int paramIdx, Instant x) throws SQLException {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+
+ private void setUuid(int paramIdx, UUID x) throws SQLException {
+ ensureNotClosed();
+ setArgumentValue(paramIdx, x);
+ }
+
+ private void setArgumentValue(int paramIdx, @Nullable Object val) throws
SQLException {
+ // The caller method ensures that this statement is not closed.
+
+ if (paramIdx < 1) {
+ throw new SQLException("Parameter index is invalid: " + paramIdx);
+ }
+
+ if (currentArguments.isEmpty()) {
+ currentArguments = new ArrayList<>(paramIdx);
+ }
+
+ while (currentArguments.size() < paramIdx) {
+ currentArguments.add(null);
+ }
+
+ currentArguments.set(paramIdx - 1, val);
+ }
+
+ List<Object> getArguments() {
+ return currentArguments;
+ }
+
+ private static void checkType(int sqlType) throws SQLException {
+ JDBCType jdbcType = JDBCType.valueOf(sqlType);
+ if (!SUPPORTED_TYPES.contains(jdbcType)) {
+ throw new
SQLFeatureNotSupportedException(SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED);
+ }
+ }
+}
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement2.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement2.java
index 53fefc6bd4b..f1612305525 100644
---
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement2.java
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement2.java
@@ -50,16 +50,18 @@ import org.jetbrains.annotations.Nullable;
*/
public class JdbcStatement2 implements Statement {
- private static final EnumSet<QueryModifier> QUERY =
EnumSet.of(QueryModifier.ALLOW_ROW_SET_RESULT);
+ static final EnumSet<QueryModifier> QUERY =
EnumSet.of(QueryModifier.ALLOW_ROW_SET_RESULT);
- private static final EnumSet<QueryModifier> DML_OR_DDL = EnumSet.of(
+ static final EnumSet<QueryModifier> DML_OR_DDL = EnumSet.of(
QueryModifier.ALLOW_AFFECTED_ROWS_RESULT,
QueryModifier.ALLOW_APPLIED_RESULT);
+ static final Set<QueryModifier> ALL = QueryModifier.ALL;
+
private static final String RETURNING_AUTO_GENERATED_KEYS_IS_NOT_SUPPORTED
=
JdbcConnection2.RETURNING_AUTO_GENERATED_KEYS_IS_NOT_SUPPORTED;
private static final String LARGE_UPDATE_NOT_SUPPORTED =
- "executeLargeUpdate not implemented";
+ "executeLargeUpdate not implemented.";
private static final String FIELD_SIZE_LIMIT_IS_NOT_SUPPORTED =
"Field size limit is not supported.";
@@ -363,7 +365,7 @@ public class JdbcStatement2 implements Statement {
public boolean execute(String sql) throws SQLException {
ensureNotClosed();
- execute0(EnumSet.allOf(QueryModifier.class),
Objects.requireNonNull(sql), ArrayUtils.OBJECT_EMPTY_ARRAY);
+ execute0(QueryModifier.ALL, Objects.requireNonNull(sql),
ArrayUtils.OBJECT_EMPTY_ARRAY);
return isQuery();
}
@@ -414,7 +416,7 @@ public class JdbcStatement2 implements Statement {
public @Nullable ResultSet getResultSet() throws SQLException {
ensureNotClosed();
- return isQuery() ? resultSet : null;
+ return resultSet;
}
/** {@inheritDoc} */
@@ -648,19 +650,17 @@ public class JdbcStatement2 implements Statement {
return iface != null && iface.isAssignableFrom(JdbcStatement2.class);
}
- /**
- * Gets the isQuery flag from the first result.
- *
- * @return isQuery flag.
- */
protected boolean isQuery() {
- if (resultSet == null) {
- return false;
- }
- return resultSet.isQuery();
+ // This method is called after statement is executed, so the reference
points to a correct result set.
+ // The statement is not expected to be used from multiple threads, so
this reference points to a correct result set.
+ // getResultSet() performs its own result set checks.
+ JdbcResultSet rs = resultSet;
+ assert rs != null;
+
+ return rs.isQuery();
}
- private void ensureNotClosed() throws SQLException {
+ void ensureNotClosed() throws SQLException {
if (isClosed()) {
throw new SQLException(STATEMENT_IS_CLOSED);
}
diff --git
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement2SelfTest.java
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement2SelfTest.java
new file mode 100644
index 00000000000..fe2cdb287b6
--- /dev/null
+++
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement2SelfTest.java
@@ -0,0 +1,727 @@
+/*
+ * 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.apache.ignite.jdbc.util.JdbcTestUtils.assertThrowsSqlException;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.io.InputStream;
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.JDBCType;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import org.apache.ignite.internal.jdbc.ConnectionProperties;
+import org.apache.ignite.internal.jdbc.ConnectionPropertiesImpl;
+import org.apache.ignite.sql.IgniteSql;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.mockito.Mockito;
+
+/**
+ * Unit tests for {@link JdbcPreparedStatement2}.
+ */
+public class JdbcPreparedStatement2SelfTest extends JdbcStatement2SelfTest {
+
+ private static final Set<JDBCType> SUPPORTED_TYPES = EnumSet.of(
+ JDBCType.BOOLEAN,
+ JDBCType.TINYINT,
+ JDBCType.SMALLINT,
+ JDBCType.INTEGER,
+ JDBCType.BIGINT,
+ JDBCType.FLOAT,
+ JDBCType.REAL,
+ JDBCType.DOUBLE,
+ JDBCType.DECIMAL,
+ JDBCType.DATE,
+ JDBCType.TIME,
+ JDBCType.TIMESTAMP,
+ JDBCType.CHAR,
+ JDBCType.VARCHAR,
+ JDBCType.BINARY,
+ JDBCType.VARBINARY,
+ JDBCType.NULL,
+ JDBCType.OTHER // UUID.
+ );
+
+ @Test
+ public void assignAndClearParameters() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps =
stmt.unwrap(JdbcPreparedStatement2.class);
+
+ stmt.setObject(1, 1);
+ stmt.setObject(3, 10L);
+
+ assertEquals(Arrays.asList(1, null, 10L), ps.getArguments());
+
+ stmt.clearParameters();
+ assertEquals(List.of(), ps.getArguments());
+
+ stmt.setObject(2, 1);
+ assertEquals(Arrays.asList(null, 1), ps.getArguments());
+
+ stmt.setObject(2, 2);
+ assertEquals(Arrays.asList(null, 2), ps.getArguments());
+
+ stmt.setObject(3, 4);
+ assertEquals(Arrays.asList(null, 2, 4), ps.getArguments());
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(JDBCType.class)
+ public void setNull(JDBCType type) throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ int jdbcSqlType = type.getVendorTypeNumber();
+
+ if (SUPPORTED_TYPES.contains(type)) {
+ stmt.setNull(1, jdbcSqlType);
+ assertFalse(ps.getArguments().isEmpty());
+ assertNull(ps.getArguments().get(0));
+
+ // Spec: If the parameter does not have a user-defined or REF
type, the given typeName is ignored.
+
+ stmt.setNull(2, jdbcSqlType, null);
+ assertFalse(ps.getArguments().isEmpty());
+ assertNull(ps.getArguments().get(1));
+
+ stmt.setNull(2, jdbcSqlType, "TypeName");
+ assertFalse(ps.getArguments().isEmpty());
+ assertNull(ps.getArguments().get(1));
+
+ } else {
+ String error = "SQL-specific types are not supported.";
+
+ expectError(() -> stmt.setNull(1, jdbcSqlType), error);
+ expectError(() -> stmt.setNull(1, jdbcSqlType, null), error);
+ expectError(() -> stmt.setNull(1, jdbcSqlType, "TypeName"),
error);
+ }
+ }
+ }
+
+ @Test
+ public void setBoolean() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ stmt.setBoolean(1, true);
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals(Boolean.TRUE, ps.getArguments().get(0));
+ }
+ }
+
+ @Test
+ public void setByte() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ stmt.setByte(1, (byte) 7);
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals((byte) 7, ps.getArguments().get(0));
+ }
+ }
+
+ @Test
+ public void setShort() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ stmt.setShort(1, (short) 123);
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals((short) 123, ps.getArguments().get(0));
+ }
+ }
+
+ @Test
+ public void setInt() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ stmt.setInt(1, 42);
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals(42, ps.getArguments().get(0));
+ }
+ }
+
+ @Test
+ public void setLong() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ stmt.setLong(1, 1322L);
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals(1322L, ps.getArguments().get(0));
+ }
+ }
+
+ @Test
+ public void setFloat() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ stmt.setFloat(1, 1.5f);
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals(1.5f, ps.getArguments().get(0));
+ }
+ }
+
+ @Test
+ public void setDouble() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ stmt.setDouble(1, 2.75d);
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals(2.75d, ps.getArguments().get(0));
+ }
+ }
+
+ @Test
+ public void setDecimal() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ BigDecimal dec = new BigDecimal("1234.5678");
+ stmt.setBigDecimal(1, dec);
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals(dec, ps.getArguments().get(0));
+
+ stmt.setBigDecimal(2, null);
+ assertTrue(ps.getArguments().size() >= 2);
+ assertNull(ps.getArguments().get(1));
+ }
+ }
+
+ @Test
+ public void setString() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ stmt.setString(1, "hello");
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals("hello", ps.getArguments().get(0));
+
+ stmt.setString(2, null);
+ assertTrue(ps.getArguments().size() >= 2);
+ assertNull(ps.getArguments().get(1));
+ }
+ }
+
+ @Test
+ public void setNationalString() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ stmt.setNString(1, "Hello");
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals("Hello", ps.getArguments().get(0));
+
+ stmt.setNString(2, null);
+ assertTrue(ps.getArguments().size() >= 2);
+ assertNull(ps.getArguments().get(1));
+ }
+ }
+
+ @Test
+ public void setBytes() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ byte[] bytes = {1, 2, 3, 4};
+ stmt.setBytes(1, bytes);
+ assertFalse(ps.getArguments().isEmpty());
+ assertArrayEquals(bytes, (byte[]) ps.getArguments().get(0));
+
+ stmt.setBytes(2, null);
+ assertTrue(ps.getArguments().size() >= 2);
+ assertNull(ps.getArguments().get(1));
+ }
+ }
+
+ @Test
+ public void setDate() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ LocalDate ld = LocalDate.of(2020, 1, 2);
+ stmt.setDate(1, Date.valueOf(ld));
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals(ld, ps.getArguments().get(0));
+
+ stmt.setDate(2, null);
+ assertTrue(ps.getArguments().size() >= 2);
+ assertNull(ps.getArguments().get(1));
+
+ stmt.setDate(3, Date.valueOf(ld), Calendar.getInstance());
+ assertTrue(ps.getArguments().size() >= 3);
+ assertEquals(ld, ps.getArguments().get(2));
+ }
+ }
+
+ @Test
+ public void setTime() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ LocalTime lt = LocalTime.of(11, 22, 33);
+ stmt.setTime(1, Time.valueOf(lt));
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals(lt, ps.getArguments().get(0));
+
+ stmt.setTime(2, null);
+ assertTrue(ps.getArguments().size() >= 2);
+ assertNull(ps.getArguments().get(1));
+
+ stmt.setTime(3, Time.valueOf(lt), Calendar.getInstance());
+ assertTrue(ps.getArguments().size() >= 3);
+ assertEquals(lt, ps.getArguments().get(2));
+ }
+ }
+
+ @Test
+ public void setTimestamp() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ LocalDateTime ldt = LocalDateTime.of(2021, 5, 6, 7, 8, 9);
+ stmt.setTimestamp(1, Timestamp.valueOf(ldt));
+ assertFalse(ps.getArguments().isEmpty());
+ assertEquals(ldt, ps.getArguments().get(0));
+
+ stmt.setTimestamp(2, null);
+ assertTrue(ps.getArguments().size() >= 2);
+ assertNull(ps.getArguments().get(1));
+
+ stmt.setTimestamp(3, Timestamp.valueOf(ldt),
Calendar.getInstance());
+ assertTrue(ps.getArguments().size() >= 3);
+ assertEquals(ldt, ps.getArguments().get(2));
+ }
+ }
+
+ @Test
+ public void setObject() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ JdbcPreparedStatement2 ps = (JdbcPreparedStatement2) stmt;
+
+ stmt.setObject(1, null);
+ assertNull(ps.getArguments().get(0));
+
+ stmt.setObject(2, (byte) 1);
+ assertEquals((byte) 1, ps.getArguments().get(1));
+
+ stmt.setObject(3, (short) 2);
+ assertEquals((short) 2, ps.getArguments().get(2));
+
+ stmt.setObject(4, 42);
+ assertEquals(42, ps.getArguments().get(3));
+
+ stmt.setObject(5, 9L);
+ assertEquals(9L, ps.getArguments().get(4));
+
+ stmt.setObject(6, 1.0f);
+ assertEquals(1.0f, ps.getArguments().get(5));
+
+ stmt.setObject(7, 2.0d);
+ assertEquals(2.0d, ps.getArguments().get(6));
+
+ stmt.setObject(8, BigDecimal.ONE);
+ assertEquals(BigDecimal.ONE, ps.getArguments().get(7));
+
+ stmt.setObject(9, "str");
+ assertEquals("str", ps.getArguments().get(8));
+
+ stmt.setObject(10, "123".getBytes(StandardCharsets.UTF_8));
+ assertArrayEquals("123".getBytes(StandardCharsets.UTF_8), (byte[])
ps.getArguments().get(9));
+
+ Date sqlDate = Date.valueOf(LocalDate.now());
+ stmt.setObject(11, sqlDate);
+ assertEquals(sqlDate.toLocalDate(), ps.getArguments().get(10));
+
+ Time sqlTime = Time.valueOf(LocalTime.now());
+ stmt.setObject(12, sqlTime);
+ assertEquals(sqlTime.toLocalTime(), ps.getArguments().get(11));
+
+ Timestamp sqlTimestamp = Timestamp.valueOf(LocalDateTime.now());
+ stmt.setObject(13, sqlTimestamp);
+ assertEquals(sqlTimestamp.toLocalDateTime(),
ps.getArguments().get(12));
+
+ UUID uuid = UUID.randomUUID();
+ stmt.setObject(14, uuid);
+ assertEquals(uuid, ps.getArguments().get(13));
+
+ // java.time classes
+
+ LocalDate date = LocalDate.now();
+ stmt.setObject(15, date);
+ assertEquals(date, ps.getArguments().get(14));
+
+ LocalTime time = LocalTime.now();
+ stmt.setObject(16, time);
+ assertEquals(time, ps.getArguments().get(15));
+
+ LocalDateTime dateTime = LocalDateTime.now();
+ stmt.setObject(17, dateTime);
+ assertEquals(dateTime, ps.getArguments().get(16));
+
+ Instant instant = Instant.now();
+ stmt.setObject(18, instant);
+ assertEquals(instant, ps.getArguments().get(17));
+
+ // Unsupported
+ expectError(
+ () -> stmt.setObject(1, new Abc()),
+ "Parameter type is not supported: " + Abc.class.getName()
+ );
+ }
+ }
+
+ private static final class Abc {
+
+ }
+
+ @Test
+ @Override
+ public void close() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+ assertFalse(stmt.isClosed());
+
+ stmt.close();
+ assertTrue(stmt.isClosed());
+
+ expectClosed(() -> stmt.setMaxFieldSize(100_000));
+ expectClosed(stmt::getMaxFieldSize);
+
+ expectClosed(() -> stmt.setEscapeProcessing(true));
+ expectClosed(() -> stmt.setEscapeProcessing(false));
+
+ expectClosed(() -> stmt.setQueryTimeout(-1));
+ expectClosed(() -> stmt.setQueryTimeout(1));
+ expectClosed(stmt::getQueryTimeout);
+
+ JdbcStatement2 stmt2 = (JdbcStatement2) stmt;
+ expectClosed(() -> stmt2.setQueryTimeout(-1));
+ expectClosed(() -> stmt2.setQueryTimeout(0));
+ expectClosed(() -> stmt2.setQueryTimeout(1));
+
+ expectClosed(stmt::cancel);
+
+ expectClosed(stmt::getWarnings);
+ expectClosed(stmt::clearWarnings);
+
+ expectClosed(() -> stmt.setCursorName("C"));
+ expectClosed(() -> stmt.setCursorName(null));
+
+ expectClosed(stmt::getResultSet);
+
+ expectClosed(stmt::getUpdateCount);
+
+ expectClosed(stmt::getMoreResults);
+
+ expectClosed(() ->
stmt.setFetchDirection(ResultSet.FETCH_FORWARD));
+ expectClosed(() ->
stmt.setFetchDirection(ResultSet.FETCH_REVERSE));
+ expectClosed(() ->
stmt.setFetchDirection(ResultSet.FETCH_UNKNOWN));
+ expectClosed(stmt::getFetchDirection);
+
+ expectClosed(() -> stmt.setFetchSize(-1));
+ expectClosed(() -> stmt.setFetchSize(0));
+ expectClosed(() -> stmt.setFetchSize(1));
+ expectClosed(stmt::getFetchSize);
+
+ expectClosed(stmt::getResultSetConcurrency);
+ expectClosed(stmt::getResultSetType);
+
+ expectClosed(stmt::clearBatch);
+
+ expectClosed(stmt::executeBatch);
+
+ expectClosed(stmt::getConnection);
+
+ expectClosed(stmt::getMoreResults);
+
+ expectClosed(() ->
stmt.getMoreResults(Statement.CLOSE_CURRENT_RESULT));
+ expectClosed(() ->
stmt.getMoreResults(Statement.KEEP_CURRENT_RESULT));
+ expectClosed(() ->
stmt.getMoreResults(Statement.CLOSE_ALL_RESULTS));
+
+ expectClosed(stmt::getGeneratedKeys);
+
+ expectClosed(stmt::getResultSetHoldability);
+
+ expectClosed(() -> stmt.setPoolable(true));
+ expectClosed(() -> stmt.setPoolable(false));
+ expectClosed(stmt::isPoolable);
+
+ expectClosed(stmt::closeOnCompletion);
+ expectClosed(stmt::isCloseOnCompletion);
+
+ // PreparedStatement specific methods
+
+ expectClosed(stmt::executeQuery);
+ expectClosed(stmt::executeUpdate);
+
+ expectClosed(() -> stmt.setNull(1,
JDBCType.INTEGER.getVendorTypeNumber()));
+ expectClosed(() -> stmt.setNull(1,
JDBCType.INTEGER.getVendorTypeNumber(), "TypeName"));
+ expectClosed(() -> stmt.setBoolean(1, true));
+ expectClosed(() -> stmt.setByte(1, (byte) 1));
+ expectClosed(() -> stmt.setShort(1, (short) 1));
+ expectClosed(() -> stmt.setInt(1, 1));
+ expectClosed(() -> stmt.setLong(1, 1));
+ expectClosed(() -> stmt.setFloat(1, 1));
+ expectClosed(() -> stmt.setDouble(1, 1));
+ expectClosed(() -> stmt.setBigDecimal(1, BigDecimal.ZERO));
+ expectClosed(() -> stmt.setString(1, "1"));
+ expectClosed(() -> stmt.setBytes(1, new byte[]{1}));
+
+ expectClosed(() -> stmt.setDate(1, Date.valueOf(LocalDate.now())));
+ expectClosed(() -> stmt.setDate(1, Date.valueOf(LocalDate.now()),
Calendar.getInstance()));
+
+ expectClosed(() -> stmt.setTime(1, Time.valueOf(LocalTime.now())));
+ expectClosed(() -> stmt.setTime(1, Time.valueOf(LocalTime.now()),
Calendar.getInstance()));
+
+ expectClosed(() -> stmt.setTimestamp(1,
Timestamp.valueOf(LocalDateTime.now())));
+ expectClosed(() -> stmt.setTimestamp(1,
Timestamp.valueOf(LocalDateTime.now()), Calendar.getInstance()));
+
+ expectClosed(() -> stmt.setAsciiStream(1,
InputStream.nullInputStream()));
+ expectClosed(() -> stmt.setAsciiStream(1,
InputStream.nullInputStream(), 1));
+ expectClosed(() -> stmt.setAsciiStream(1,
InputStream.nullInputStream(), 1L));
+
+ //noinspection deprecation
+ expectClosed(() -> stmt.setUnicodeStream(1,
InputStream.nullInputStream(), 1));
+
+ expectClosed(() -> stmt.setBinaryStream(1,
InputStream.nullInputStream()));
+ expectClosed(() -> stmt.setBinaryStream(1,
InputStream.nullInputStream(), 10L));
+ expectClosed(() -> stmt.setBinaryStream(1,
InputStream.nullInputStream(), 10));
+
+ expectClosed(stmt::clearParameters);
+ expectClosed(() -> stmt.setObject(1, 1));
+ // not supported
+ expectClosed(() -> stmt.setObject(1, 1,
JDBCType.INTEGER.getVendorTypeNumber()));
+ // // not supported
+ expectClosed(() -> stmt.setObject(1, 1, JDBCType.INTEGER));
+ // not supported
+ expectClosed(() -> stmt.setObject(1, 1,
JDBCType.VARCHAR.getVendorTypeNumber(), 10));
+ // not supported
+ expectClosed(() -> stmt.setObject(1, 1, JDBCType.VARCHAR, 10));
+
+ expectClosed(() -> stmt.setURL(1, new URL("https://test.com")));
+
+ expectClosed(() -> stmt.setCharacterStream(1, new
StringReader("1")));
+ expectClosed(() -> stmt.setCharacterStream(1, new
StringReader("1"), 10));
+ expectClosed(() -> stmt.setCharacterStream(1, new
StringReader("1"), 10L));
+
+ expectClosed(() -> stmt.setRef(1, Mockito.mock(Ref.class)));
+
+ expectClosed(() -> stmt.setBlob(1, Mockito.mock(Blob.class)));
+ expectClosed(() -> stmt.setBlob(1, InputStream.nullInputStream()));
+ expectClosed(() -> stmt.setBlob(1, InputStream.nullInputStream(),
10));
+
+ expectClosed(() -> stmt.setClob(1, Mockito.mock(Clob.class)));
+ expectClosed(() -> stmt.setClob(1, new StringReader("1")));
+ expectClosed(() -> stmt.setClob(1, new StringReader("1"), 10));
+
+ expectClosed(() -> stmt.setArray(1, Mockito.mock(Array.class)));
+
+ expectClosed(stmt::getMetaData);
+ expectClosed(stmt::getParameterMetaData);
+
+ expectClosed(() -> stmt.setRowId(1, Mockito.mock(RowId.class)));
+
+ expectClosed(() -> stmt.setNCharacterStream(1, new
StringReader("1")));
+ expectClosed(() -> stmt.setNCharacterStream(1, new
StringReader("1"), 10));
+
+ expectClosed(() -> stmt.setNClob(1, new StringReader("1")));
+ expectClosed(() -> stmt.setNClob(1, new StringReader("1"), 10));
+ expectClosed(() -> stmt.setNClob(1, Mockito.mock(NClob.class)));
+
+ expectClosed(() -> stmt.setSQLXML(1, Mockito.mock(SQLXML.class)));
+
+ expectClosed(stmt::executeLargeUpdate);
+ expectClosed(stmt::executeLargeBatch);
+ }
+ }
+
+ @Test
+ public void executeSql() throws SQLException {
+ try (Statement stmt = createStatement()) {
+ expectError(() -> stmt.execute("UPDATE t SET c = 1"),
+ "The method 'execute(String)' is called on
PreparedStatement instance.");
+
+ expectError(() -> stmt.execute("UPDATE t SET c = 1",
Statement.RETURN_GENERATED_KEYS),
+ "The method 'execute(String, int)' is called on
PreparedStatement instance.");
+
+ expectError(() -> stmt.execute("UPDATE t SET c = 1", new int[]{1}),
+ "The method 'execute(String, int[])' is called on
PreparedStatement instance.");
+
+ expectError(() -> stmt.execute("UPDATE t SET c = 1", new
String[]{"id"}),
+ "The method 'execute(String, String[]) is called on
PreparedStatement instance.");
+ }
+ }
+
+ @Test
+ @Override
+ public void updateWithColumns() throws SQLException {
+ try (Statement stmt = createStatement()) {
+ expectError(() -> stmt.executeUpdate("UPDATE t SET c = 1"),
+ "The method 'executeUpdate(String)' is called on
PreparedStatement instance.");
+
+ expectError(() -> stmt.executeUpdate("UPDATE t SET c = 1",
Statement.RETURN_GENERATED_KEYS),
+ "The method 'executeUpdate(String, int)' is called on
PreparedStatement instance.");
+
+ expectError(() -> stmt.executeUpdate("UPDATE t SET c = 1", new
int[]{1}),
+ "The method 'executeUpdate(String, int[])' is called on
PreparedStatement instance.");
+
+ expectError(() -> stmt.executeUpdate("UPDATE t SET c = 1", new
String[]{"id"}),
+ "The method 'executeUpdate(String, String[])' is called on
PreparedStatement instance.");
+ }
+ }
+
+ @Test
+ public void notSupportedMethods() throws SQLException {
+ try (PreparedStatement stmt = createStatement()) {
+
+ String conversionError = "Conversion to target sql type is not
supported.";
+ expectError(() -> stmt.setObject(1, 1,
JDBCType.INTEGER.getVendorTypeNumber()), conversionError);
+ expectError(() -> stmt.setObject(1, 1, JDBCType.INTEGER),
conversionError);
+ expectError(() -> stmt.setObject(1, 1,
JDBCType.VARCHAR.getVendorTypeNumber(), 10), conversionError);
+ expectError(() -> stmt.setObject(1, 1, JDBCType.VARCHAR, 10),
conversionError);
+
+ String sqlTypeError = "SQL-specific types are not supported.";
+ expectError(() -> stmt.setURL(1, new URL("https://test.com")),
sqlTypeError);
+
+ expectError(() -> stmt.setRef(1, Mockito.mock(Ref.class)),
sqlTypeError);
+
+ expectError(() -> stmt.setBlob(1, Mockito.mock(Blob.class)),
sqlTypeError);
+ expectError(() -> stmt.setBlob(1, InputStream.nullInputStream()),
sqlTypeError);
+ expectError(() -> stmt.setBlob(1, InputStream.nullInputStream(),
10), sqlTypeError);
+
+ expectError(() -> stmt.setClob(1, Mockito.mock(Clob.class)),
sqlTypeError);
+ expectError(() -> stmt.setClob(1, new StringReader("1")),
sqlTypeError);
+ expectError(() -> stmt.setClob(1, new StringReader("1"), 10),
sqlTypeError);
+
+ expectError(() -> stmt.setArray(1, Mockito.mock(Array.class)),
sqlTypeError);
+
+ expectError(() -> stmt.setRowId(1, Mockito.mock(RowId.class)),
sqlTypeError);
+
+ expectError(() -> stmt.setNClob(1, new StringReader("1")),
sqlTypeError);
+ expectError(() -> stmt.setNClob(1, new StringReader("1"), 10),
sqlTypeError);
+ expectError(() -> stmt.setNClob(1, Mockito.mock(NClob.class)),
sqlTypeError);
+
+ expectError(() -> stmt.setSQLXML(1, Mockito.mock(SQLXML.class)),
sqlTypeError);
+
+ String streamTypeError = "Streams are not supported";
+ expectError(() -> stmt.setCharacterStream(1, new
StringReader("1")), sqlTypeError);
+ expectError(() -> stmt.setCharacterStream(1, new
StringReader("1"), 10), sqlTypeError);
+ expectError(() -> stmt.setCharacterStream(1, new
StringReader("1"), 10L), sqlTypeError);
+
+ expectError(() -> stmt.setNCharacterStream(1, new
StringReader("1")), streamTypeError);
+ expectError(() -> stmt.setNCharacterStream(1, new
StringReader("1"), 10), streamTypeError);
+ expectError(() -> stmt.setNCharacterStream(1, new
StringReader("1"), 10L), streamTypeError);
+
+ expectError(() -> stmt.setAsciiStream(1,
InputStream.nullInputStream()), streamTypeError);
+ expectError(() -> stmt.setAsciiStream(1,
InputStream.nullInputStream(), 10), streamTypeError);
+ expectError(() -> stmt.setAsciiStream(1,
InputStream.nullInputStream(), 10L), streamTypeError);
+
+ expectError(() -> stmt.setBinaryStream(1,
InputStream.nullInputStream()), streamTypeError);
+ expectError(() -> stmt.setBinaryStream(1,
InputStream.nullInputStream(), 10), streamTypeError);
+ expectError(() -> stmt.setBinaryStream(1,
InputStream.nullInputStream(), 10L), streamTypeError);
+
+ //noinspection deprecation
+ expectError(() -> stmt.setUnicodeStream(1,
InputStream.nullInputStream(), 10), streamTypeError);
+
+ expectError(stmt::getMetaData, "ResultSet metadata for prepared
statement is not supported.");
+ expectError(stmt::getParameterMetaData, "Parameter metadata is not
supported.");
+
+ expectError(stmt::executeLargeUpdate, "executeLargeUpdate not
implemented.");
+ expectError(stmt::executeLargeBatch, "executeLargeBatch not
implemented.");
+ }
+ }
+
+ @Test
+ @Override
+ public void largeUpdateMethods() throws SQLException {
+ try (Statement stmt = createStatement()) {
+ expectError(() -> stmt.executeLargeUpdate("UPDATE t SET val=2"),
+ "The method 'executeLargeUpdate(String)' is called on
PreparedStatement instance.");
+
+ expectError(() -> stmt.executeLargeUpdate("UPDATE t SET val=2",
Statement.RETURN_GENERATED_KEYS),
+ "The method 'executeLargeUpdate(String, int)' is called on
PreparedStatement instance.");
+
+ expectError(() -> stmt.executeLargeUpdate("UPDATE t SET val=2",
new int[]{0}),
+ "The method 'executeLargeUpdate(String, int[])' is called
on PreparedStatement instance.");
+
+ expectError(() -> stmt.executeLargeUpdate("UPDATE t SET val=2",
new String[]{"C1"}),
+ "The method 'executeLargeUpdate(String, String[])' is
called on PreparedStatement instance.");
+ }
+ }
+
+ private static void expectError(Executable executable, String message) {
+ assertThrowsSqlException(SQLException.class, message, executable);
+ }
+
+ @Override
+ protected PreparedStatement createStatement() throws SQLException {
+ Connection connection = Mockito.mock(Connection.class);
+ JdbcConnection2 connection2 = Mockito.mock(JdbcConnection2.class);
+
+ ConnectionProperties properties = new ConnectionPropertiesImpl();
+
+ when(connection.unwrap(JdbcConnection2.class)).thenReturn(connection2);
+ when(connection2.properties()).thenReturn(properties);
+
+ return createStatement(connection);
+ }
+
+ @Override
+ protected PreparedStatement createStatement(Connection connection) {
+ IgniteSql igniteSql = Mockito.mock(IgniteSql.class);
+ return new JdbcPreparedStatement2(connection, igniteSql, "PUBLIC",
ResultSet.HOLD_CURSORS_OVER_COMMIT, "SELECT 1");
+ }
+}
diff --git
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatement2SelfTest.java
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatement2SelfTest.java
index 086dd9843ac..0f1f8b87070 100644
---
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatement2SelfTest.java
+++
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatement2SelfTest.java
@@ -434,7 +434,7 @@ public class JdbcStatement2SelfTest extends
BaseIgniteAbstractTest {
}
}
- private static Statement createStatement() throws SQLException {
+ protected Statement createStatement() throws SQLException {
Connection connection = Mockito.mock(Connection.class);
JdbcConnection2 connection2 = Mockito.mock(JdbcConnection2.class);
@@ -446,12 +446,12 @@ public class JdbcStatement2SelfTest extends
BaseIgniteAbstractTest {
return createStatement(connection);
}
- private static Statement createStatement(Connection connection) {
+ protected Statement createStatement(Connection connection) {
IgniteSql igniteSql = Mockito.mock(IgniteSql.class);
return new JdbcStatement2(connection, igniteSql, "PUBLIC",
ResultSet.HOLD_CURSORS_OVER_COMMIT);
}
- private static void expectClosed(Executable method) {
+ static void expectClosed(Executable method) {
assertThrowsSqlException(SQLException.class, "Statement is closed.",
method);
}
}