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

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


The following commit(s) were added to refs/heads/main by this push:
     new 9e504e831d IGNITE-19411 Sql. Jdbc add unit tests for PreparedStatement 
set parameter methods (#2017)
9e504e831d is described below

commit 9e504e831d09260fc292260fcc79ecdf686f435f
Author: Max Zhuravkov <[email protected]>
AuthorDate: Mon May 8 12:40:31 2023 +0300

    IGNITE-19411 Sql. Jdbc add unit tests for PreparedStatement set parameter 
methods (#2017)
---
 modules/jdbc/build.gradle                          |   3 +
 .../internal/jdbc/JdbcPreparedStatement.java       |  89 ++++-
 .../internal/jdbc/PreparedStatementParamsTest.java | 433 +++++++++++++++++++++
 3 files changed, 518 insertions(+), 7 deletions(-)

diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle
index 13e72bf477..d26ee4f1d5 100644
--- a/modules/jdbc/build.gradle
+++ b/modules/jdbc/build.gradle
@@ -35,6 +35,9 @@ dependencies {
 
     annotationProcessor libs.auto.service
 
+    testImplementation libs.mockito.core
+    testImplementation libs.hamcrest.core
+
     integrationTestImplementation testFixtures(project(":ignite-api"))
     integrationTestImplementation testFixtures(project(":ignite-core"))
     integrationTestImplementation project(":ignite-runner")
diff --git 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcPreparedStatement.java
 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcPreparedStatement.java
index d10709347b..ecc4e27d5b 100644
--- 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcPreparedStatement.java
+++ 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcPreparedStatement.java
@@ -28,6 +28,7 @@ import java.sql.BatchUpdateException;
 import java.sql.Blob;
 import java.sql.Clob;
 import java.sql.Date;
+import java.sql.JDBCType;
 import java.sql.NClob;
 import java.sql.ParameterMetaData;
 import java.sql.PreparedStatement;
@@ -40,10 +41,14 @@ import java.sql.SQLFeatureNotSupportedException;
 import java.sql.SQLXML;
 import java.sql.Time;
 import java.sql.Timestamp;
+import java.sql.Types;
 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 java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
 import org.apache.ignite.internal.jdbc.proto.IgniteQueryErrorCode;
@@ -52,12 +57,33 @@ import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcBatchExecuteResult;
 import 
org.apache.ignite.internal.jdbc.proto.event.JdbcBatchPreparedStmntRequest;
 import org.apache.ignite.internal.util.CollectionUtils;
-import org.apache.ignite.internal.util.StringUtils;
 
 /**
  * Jdbc prepared statement implementation.
  */
 public class JdbcPreparedStatement extends JdbcStatement 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.OTHER // UUID.
+    );
+
     /** SQL query. */
     private final String sql;
 
@@ -109,7 +135,7 @@ public class JdbcPreparedStatement extends JdbcStatement 
implements PreparedStat
 
         closeResults();
 
-        if (CollectionUtils.nullOrEmpty(batchedArgs) || 
StringUtils.nullOrBlank(sql)) {
+        if (CollectionUtils.nullOrEmpty(batchedArgs)) {
             return INT_EMPTY_ARRAY;
         }
 
@@ -248,13 +274,17 @@ public class JdbcPreparedStatement extends JdbcStatement 
implements PreparedStat
     /** {@inheritDoc} */
     @Override
     public void setNull(int paramIdx, int sqlType) throws SQLException {
+        checkType(sqlType);
+
         setArgument(paramIdx, null);
     }
 
     /** {@inheritDoc} */
     @Override
     public void setNull(int paramIdx, int sqlType, String typeName) throws 
SQLException {
-        setNull(paramIdx, sqlType);
+        checkType(sqlType);
+
+        setArgument(paramIdx, null);
     }
 
     /** {@inheritDoc} */
@@ -421,13 +451,47 @@ public class JdbcPreparedStatement extends JdbcStatement 
implements PreparedStat
     /** {@inheritDoc} */
     @Override
     public void setObject(int paramIdx, Object x, int targetSqlType) throws 
SQLException {
-        setArgument(paramIdx, x); //TODO Do we support objects?
+        checkType(targetSqlType);
+
+        throw new SQLFeatureNotSupportedException("Conversion to target sql 
type is not supported.");
     }
 
     /** {@inheritDoc} */
     @Override
     public void setObject(int paramIdx, Object x) throws SQLException {
-        setArgument(paramIdx, x); //TODO Do we support objects?
+        if (x == null) {
+            setNull(paramIdx, Types.OTHER);
+        } 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 UUID) {
+            setArgument(paramIdx, x);
+        } else {
+            throw new SQLFeatureNotSupportedException("Parameter is not 
supported: " + x + " <" + x.getClass().getTypeName() + ">");
+        }
     }
 
     /** {@inheritDoc} */
@@ -435,7 +499,7 @@ public class JdbcPreparedStatement extends JdbcStatement 
implements PreparedStat
     public void setObject(int paramIdx, Object x, int targetSqlType, int 
scaleOrLen) throws SQLException {
         ensureNotClosed();
 
-        throw new SQLFeatureNotSupportedException("Objects are not 
supported.");
+        throw new SQLFeatureNotSupportedException("Conversion to target sql 
type is not supported.");
     }
 
     /** {@inheritDoc} */
@@ -634,7 +698,7 @@ public class JdbcPreparedStatement extends JdbcStatement 
implements PreparedStat
      * Sets query argument value.
      *
      * @param paramIdx Index.
-     * @param val      Value.
+     * @param val Value.
      * @throws SQLException If index is invalid.
      */
     private void setArgument(int paramIdx, Object val) throws SQLException {
@@ -655,6 +719,10 @@ public class JdbcPreparedStatement extends JdbcStatement 
implements PreparedStat
         currentArgs.set(paramIdx - 1, val);
     }
 
+    List<Object> getArguments() {
+        return currentArgs;
+    }
+
     /**
      * Execute query with arguments and nullify them afterwards.
      *
@@ -666,4 +734,11 @@ public class JdbcPreparedStatement extends JdbcStatement 
implements PreparedStat
 
         currentArgs = null;
     }
+
+    private static void checkType(int sqlType) throws SQLException {
+        JDBCType jdbcType = JDBCType.valueOf(sqlType);
+        if (!SUPPORTED_TYPES.contains(jdbcType)) {
+            throw new SQLFeatureNotSupportedException("Type is not supported: 
" + sqlType);
+        }
+    }
 }
diff --git 
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/PreparedStatementParamsTest.java
 
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/PreparedStatementParamsTest.java
new file mode 100644
index 0000000000..3a0fc139a6
--- /dev/null
+++ 
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/PreparedStatementParamsTest.java
@@ -0,0 +1,433 @@
+/*
+ * 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.jdbc;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.JDBCType;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLType;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.time.LocalTime;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeSet;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Stream;
+import org.apache.ignite.internal.jdbc.proto.JdbcQueryEventHandler;
+import org.apache.ignite.internal.jdbc.proto.event.JdbcConnectResult;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mockito;
+
+/**
+ * Unit tests for PreparedStatement methods.
+ */
+public class PreparedStatementParamsTest {
+
+    /** Values for supported JDBC types. */
+    private static final Map<JDBCType, Object> SUPPORTED_TYPES = Map.ofEntries(
+            Map.entry(JDBCType.BOOLEAN, true),
+            Map.entry(JDBCType.TINYINT, (byte) 1),
+            Map.entry(JDBCType.SMALLINT, (short) 1),
+            Map.entry(JDBCType.INTEGER, 1),
+            Map.entry(JDBCType.BIGINT, 1L),
+            Map.entry(JDBCType.FLOAT, 1.0f),
+            Map.entry(JDBCType.REAL, 1.0f),
+            Map.entry(JDBCType.DOUBLE, 1.0d),
+            Map.entry(JDBCType.DECIMAL, new BigDecimal("123")),
+            Map.entry(JDBCType.CHAR, "123"),
+            Map.entry(JDBCType.VARCHAR, "123"),
+            Map.entry(JDBCType.BINARY, new byte[]{1, 2, 3}),
+            Map.entry(JDBCType.VARBINARY, new byte[]{1, 2, 3}),
+            Map.entry(JDBCType.DATE, new Date(1)),
+            Map.entry(JDBCType.TIME, Time.valueOf(LocalTime.NOON)),
+            Map.entry(JDBCType.TIMESTAMP, Timestamp.valueOf("2000-01-01 
00:00:00.000")),
+            Map.entry(JDBCType.OTHER, new UUID(1, 1))
+    );
+
+    private JdbcConnection conn;
+
+    @BeforeEach
+    public void initConnection() throws SQLException {
+        JdbcQueryEventHandler handler = 
Mockito.mock(JdbcQueryEventHandler.class);
+        
when(handler.connect()).thenReturn(CompletableFuture.completedFuture(new 
JdbcConnectResult(1)));
+
+        conn = new JdbcConnection(handler, new ConnectionPropertiesImpl());
+    }
+
+    /** {@link PreparedStatement#clearParameters()} clears parameter list. */
+    @Test
+    public void clearParameters() throws SQLException {
+        try (PreparedStatement pstmt = conn.prepareStatement("SELECT 1")) {
+            pstmt.setObject(1, 1);
+
+            JdbcPreparedStatement ignitePstmt = 
pstmt.unwrap(JdbcPreparedStatement.class);
+            assertEquals(1, ignitePstmt.getArguments().size());
+
+            pstmt.clearParameters();
+            assertNull(ignitePstmt.getArguments(), "parameters has not been 
cleared");
+        }
+    }
+
+    /**
+     * Set {@code boolean} parameter via {@link 
PreparedStatement#setBoolean(int, boolean)}.
+     */
+    @Test
+    public void testSetBoolean() throws SQLException {
+        boolean value = value(Boolean.class, JDBCType.BOOLEAN);
+        checkParameter(PreparedStatement::setBoolean, value);
+    }
+
+    /**
+     * Set {@code byte} parameter via {@link PreparedStatement#setByte(int, 
byte)}.
+     */
+    @Test
+    public void testSetByte() throws SQLException {
+        byte value = value(Byte.class, JDBCType.TINYINT);
+        checkParameter(PreparedStatement::setByte, value);
+    }
+
+    /**
+     * Set {@code short} parameter via {@link PreparedStatement#setShort(int, 
short)}.
+     */
+    @Test
+    public void testSetShort() throws SQLException {
+        short value = value(Short.class, JDBCType.SMALLINT);
+        checkParameter(PreparedStatement::setShort, value, value);
+    }
+
+    /**
+     * Set {@code int} parameter via {@link PreparedStatement#setInt(int, 
int)}.
+     */
+    @Test
+    public void testSetInt() throws SQLException {
+        int value = value(Integer.class, JDBCType.INTEGER);
+        checkParameter(PreparedStatement::setInt, value);
+    }
+
+    /**
+     * Set {@code long} parameter via {@link PreparedStatement#setLong(int, 
long)}.
+     */
+    @Test
+    public void testSetLong() throws SQLException {
+        long value = value(Long.class, JDBCType.BIGINT);
+        checkParameter(PreparedStatement::setLong, value);
+    }
+
+    /**
+     * Set {@code float} parameter via {@link PreparedStatement#setFloat(int, 
float)}.
+     */
+    @Test
+    public void testSetFloat() throws SQLException {
+        float value = value(Float.class, JDBCType.FLOAT);
+        checkParameter(PreparedStatement::setFloat, value);
+    }
+
+    /**
+     * Set {@code double} parameter via {@link 
PreparedStatement#setDouble(int, double)}.
+     */
+    @Test
+    public void testSetDouble() throws SQLException {
+        double value = value(Double.class, JDBCType.DOUBLE);
+        checkParameter(PreparedStatement::setDouble, value);
+    }
+
+    /**
+     * Set {@code BigDecimal} parameter via {@link 
PreparedStatement#setBigDecimal(int, BigDecimal)}.
+     */
+    @Test
+    public void testSetBigDecimal() throws SQLException {
+        BigDecimal value = value(BigDecimal.class, JDBCType.DECIMAL);
+        checkParameter(PreparedStatement::setBigDecimal, value);
+    }
+
+    /**
+     * Set {@code string} parameter via {@link 
PreparedStatement#setString(int, String)}.
+     */
+    @Test
+    public void testSetString() throws SQLException {
+        String value = value(String.class, JDBCType.VARCHAR);
+        checkParameter(PreparedStatement::setString, value);
+    }
+
+    /**
+     * Set {@code byte array} parameter via {@link 
PreparedStatement#setBytes(int, byte[])}.
+     */
+    @Test
+    public void testSetBytes() throws SQLException {
+        byte[] bytes = value(byte[].class, JDBCType.VARBINARY);
+        checkParameter(PreparedStatement::setBytes, bytes);
+    }
+
+    /**
+     * Set {@link Date} parameter via {@link PreparedStatement#setDate(int, 
Date)}.
+     */
+    @Test
+    public void testSetDate() throws SQLException {
+        Date value = value(Date.class, JDBCType.DATE);
+        checkParameter(PreparedStatement::setDate, value);
+    }
+
+    /**
+     * Set {@link Time} parameter via {@link PreparedStatement#setTime(int, 
Time)}}.
+     */
+    @Test
+    public void testSetTime() throws SQLException {
+        Time time = value(Time.class, JDBCType.TIME);
+        checkParameter(PreparedStatement::setTime, time);
+    }
+
+    /**
+     * Set {@link Timestamp} parameter via {@link 
PreparedStatement#setTimestamp(int, Timestamp)}}.
+     */
+    @Test
+    public void testSetTimestamp() throws SQLException {
+        Timestamp timestamp = value(Timestamp.class, JDBCType.TIMESTAMP);
+        checkParameter(PreparedStatement::setTimestamp, timestamp);
+    }
+
+    /** {@link PreparedStatement#setObject(int, Object)} for all supported 
types. */
+    @ParameterizedTest(name = "setObject: {0} {2}")
+    @MethodSource("jdbcTypeValueTypeNames")
+    public void testSetObject(JDBCType jdbcType, Object value, String 
javaTypeName) throws SQLException {
+        checkParameter((p, idx, v) -> p.setObject(idx, value), value);
+    }
+
+    /**
+     * Call to {@link PreparedStatement#setObject(int, Object)}.
+     */
+    @Test
+    public void testSetObjectWithTypeConversion() {
+        int typeCode = JDBCType.INTEGER.getVendorTypeNumber();
+        Object value = new Object();
+
+        checkFeatureIsNotSupported((s) -> s.setObject(1, value, typeCode), 
"Conversion to target sql type is not supported.");
+    }
+
+    /**
+     * {@link PreparedStatement#setObject(int, Object, int, int)} is not 
supported.
+     */
+    @Test
+    public void testSetObjectWithScale() {
+        int typeCode = JDBCType.INTEGER.getVendorTypeNumber();
+        Object value = new Object();
+
+        checkFeatureIsNotSupported((s) -> s.setObject(1, value, typeCode, 1), 
"Conversion to target sql type is not supported.");
+    }
+
+    /**
+     * {@link PreparedStatement#setObject(int, Object, SQLType)} is not 
supported.
+     */
+    @Test
+    public void testSetObjectWitSqlTypeIsNotSupported() {
+        UUID uuid = new UUID(0, 0);
+        SQLType sqlType = Mockito.mock(SQLType.class);
+
+        SQLException err = checkSetParameterFails((s) -> s.setObject(1, uuid, 
sqlType, 1));
+        assertInstanceOf(SQLFeatureNotSupportedException.class, err);
+    }
+
+    /**
+     * {@link PreparedStatement#setObject(int, Object)} throws an exception 
when called with unsupported type.
+     */
+    @Test
+    public void testSetObjectRejectUnknownValue() {
+        Object value = new Object();
+
+        checkFeatureIsNotSupported((s) -> s.setObject(1, value), "Parameter is 
not supported");
+    }
+
+    /** {@code setNull} for supported types. */
+    @ParameterizedTest(name = "setNull: {1}")
+    @MethodSource("igniteTypeTypeNames")
+    public void testSetNull(JDBCType igniteType, String typeName) throws 
SQLException {
+        int typeCode = igniteType.getVendorTypeNumber();
+
+        checkParameter((p, idx, v) -> p.setNull(idx, typeCode), null);
+
+        // setNull with typename
+        checkParameter((p, idx, v) -> p.setNull(idx, typeCode, typeName), 
null);
+    }
+
+
+    /** {@code setNull} does not support types not allowed by spec. */
+    @ParameterizedTest(name = " setNull(typeId): {0}")
+    @MethodSource("typesDoNotSupportSetNull")
+    public void testSetNullForNotSupportedTypes(JDBCType jdbcType) {
+        int typeCode = jdbcType.getVendorTypeNumber();
+
+        SQLException err = checkSetParameterFails((s) -> s.setNull(1, 
typeCode));
+        assertInstanceOf(SQLFeatureNotSupportedException.class, err);
+        assertThat(err.getMessage(), containsString("Type is not supported"));
+
+        // setNull with typename
+        SQLException err2 = checkSetParameterFails((s) -> s.setNull(1, 
typeCode, jdbcType.getName()));
+        assertInstanceOf(SQLFeatureNotSupportedException.class, err2);
+        assertThat(err2.getMessage(), containsString("Type is not supported"));
+    }
+
+    private <T> void checkParameter(SetParameter<T> set, T value) throws 
SQLException {
+        checkParameter(set, value, value);
+    }
+
+    /** Sets the first parameter and checks that that parameter is set. */
+    private <T> void checkParameter(SetParameter<T> set, T value, Object 
expected) throws SQLException {
+        try (PreparedStatement pstmt = conn.prepareStatement("SELECT ?")) {
+            set.setParameter(pstmt, 1, value);
+
+            JdbcPreparedStatement igniteStmt = 
pstmt.unwrap(JdbcPreparedStatement.class);
+            Object paramValue = igniteStmt.getArguments().get(0);
+
+            if (expected == null) {
+                assertNull(paramValue);
+            } else if (paramValue.getClass().isArray()) {
+                Object[] expectedArray = toObjects(expected);
+                Object[] actualArray = toObjects(paramValue);
+
+                // Compare array types
+                assertSame(expected.getClass(), paramValue.getClass(), "array 
type");
+                assertArrayEquals(expectedArray, actualArray, "array values");
+            } else {
+                assertEquals(expected, paramValue);
+            }
+        }
+    }
+
+    /** Expects that the given action that uses {@code PreparedStatement} 
throws an {@link SQLException}. */
+    private SQLException checkSetParameterFails(StatementConsumer action) {
+        return Assertions.assertThrows(SQLException.class, () -> {
+            try (PreparedStatement pstmt = conn.prepareStatement("SELECT ?")) {
+                action.consume(pstmt);
+            }
+        });
+    }
+
+    /**
+     * Expects that the given action that uses {@code PreparedStatement} 
throws an {@link SQLFeatureNotSupportedException}
+     * and an error message contains {@code messagePart}.
+     */
+    private void checkFeatureIsNotSupported(StatementConsumer action, String 
messagePart) {
+        SQLFeatureNotSupportedException err = 
Assertions.assertThrows(SQLFeatureNotSupportedException.class, () -> {
+            try (PreparedStatement pstmt = conn.prepareStatement("SELECT ?")) {
+                action.consume(pstmt);
+            }
+        });
+        assertThat(err.getMessage(), containsString(messagePart));
+    }
+
+    private static Stream<Arguments> jdbcTypeValueTypeNames() {
+        return SUPPORTED_TYPES.entrySet().stream()
+                .sorted(Comparator.comparing(a -> a.getKey().getName()))
+                .map(e -> {
+                    JDBCType jdbcType = e.getKey();
+                    Object value = e.getValue();
+                    String javaTypeName = value.getClass().getTypeName();
+
+                    return Arguments.of(jdbcType, value, javaTypeName);
+                });
+    }
+
+    private static Stream<Arguments> igniteTypeTypeNames() {
+        return Arrays.stream(JDBCType.values())
+                .filter(SUPPORTED_TYPES::containsKey)
+                .map(t -> Arguments.of(t, t.getName()));
+    }
+
+    private static Stream<Arguments> typesDoNotSupportSetNull() {
+
+        // Types that are not allowed in setNull spec
+        TreeSet<SQLType> result = new TreeSet<>(Arrays.asList(
+                JDBCType.ARRAY,
+                JDBCType.BLOB,
+                JDBCType.CLOB,
+                JDBCType.DATALINK,
+                JDBCType.JAVA_OBJECT,
+                JDBCType.NCHAR,
+                JDBCType.NCLOB,
+                JDBCType.LONGNVARCHAR,
+                JDBCType.REF,
+                JDBCType.ROWID,
+                JDBCType.SQLXML,
+                JDBCType.STRUCT
+        ));
+
+        // Types that are not supported by JDBC driver implementation
+        for (JDBCType jdbcType : JDBCType.values()) {
+            if (!SUPPORTED_TYPES.containsKey(jdbcType)) {
+                result.add(jdbcType);
+            }
+        }
+
+        return result.stream()
+                .sorted(Comparator.comparing(SQLType::getName))
+                .map(t -> Arguments.of(t.getName(), t));
+    }
+
+    private static <T> T value(Class<T> javaType, JDBCType jdbcType) {
+        Object value = SUPPORTED_TYPES.get(jdbcType);
+        Objects.requireNonNull(value, "No value for " + javaType);
+
+        return javaType.cast(value);
+    }
+
+    private static Object[] toObjects(Object input) {
+        Class<?> inputClass = input.getClass();
+        if (!inputClass.isArray()) {
+            throw new IllegalArgumentException("Expected array but got " + 
inputClass.getTypeName());
+        }
+
+        Object[] out = new Object[Array.getLength(input)];
+        for (var i = 0; i < out.length; i++) {
+            out[i] = Array.get(input, i);
+        }
+        return out;
+    }
+
+    @FunctionalInterface
+    interface SetParameter<T> {
+
+        void setParameter(PreparedStatement s, int idx, T value) throws 
SQLException;
+    }
+
+    @FunctionalInterface
+    interface StatementConsumer {
+
+        void consume(PreparedStatement s) throws SQLException;
+    }
+}

Reply via email to