From 51b6718faa0596c31bbd36f00a648c9916af063b Mon Sep 17 00:00:00 2001 From: "peco-engineer-bot[bot]" <3815206+peco-engineer-bot[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2026 21:33:11 +0000 Subject: [PATCH] [coverage] Conformance findings: PARAMQUERY-021 (#1599) Signed-off-by: peco-engineer-bot[bot] <3815206+peco-engineer-bot[bot]@users.noreply.github.com> --- .../jdbc/common/util/DatabricksTypeUtil.java | 4 +- .../common/util/DatabricksTypeUtilTest.java | 4 +- .../e2e/ParameterBindTypeTests.java | 66 +++++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/databricks/jdbc/integration/e2e/ParameterBindTypeTests.java diff --git a/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java b/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java index ce1631845..dbc8275d7 100644 --- a/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java +++ b/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java @@ -473,9 +473,9 @@ public static String getDatabricksTypeFromSQLType(int sqlType) { case Types.BOOLEAN: return BOOLEAN; case Types.DOUBLE: + case Types.FLOAT: // JDBC FLOAT is a synonym for DOUBLE (8-byte double precision) return DOUBLE; - case Types.FLOAT: - case Types.REAL: // REAL is float(24) + case Types.REAL: // REAL is float(24), 4-byte single precision return FLOAT; case Types.INTEGER: return INT; diff --git a/src/test/java/com/databricks/jdbc/common/util/DatabricksTypeUtilTest.java b/src/test/java/com/databricks/jdbc/common/util/DatabricksTypeUtilTest.java index 4ae0c5945..6a3662735 100644 --- a/src/test/java/com/databricks/jdbc/common/util/DatabricksTypeUtilTest.java +++ b/src/test/java/com/databricks/jdbc/common/util/DatabricksTypeUtilTest.java @@ -218,7 +218,9 @@ void testGetDatabricksTypeFromSQLType() { Map.entry(Types.DECIMAL, DatabricksTypeUtil.DECIMAL), Map.entry(Types.BOOLEAN, DatabricksTypeUtil.BOOLEAN), Map.entry(Types.DOUBLE, DatabricksTypeUtil.DOUBLE), - Map.entry(Types.FLOAT, DatabricksTypeUtil.FLOAT), + // JDBC spec Appendix B: Types.FLOAT is a synonym for DOUBLE (8-byte), + // only Types.REAL is 4-byte single precision. + Map.entry(Types.FLOAT, DatabricksTypeUtil.DOUBLE), Map.entry(Types.REAL, DatabricksTypeUtil.FLOAT), Map.entry(Types.TIMESTAMP, DatabricksTypeUtil.TIMESTAMP_NTZ), Map.entry(Types.TIMESTAMP_WITH_TIMEZONE, DatabricksTypeUtil.TIMESTAMP), diff --git a/src/test/java/com/databricks/jdbc/integration/e2e/ParameterBindTypeTests.java b/src/test/java/com/databricks/jdbc/integration/e2e/ParameterBindTypeTests.java new file mode 100644 index 000000000..cc622cae8 --- /dev/null +++ b/src/test/java/com/databricks/jdbc/integration/e2e/ParameterBindTypeTests.java @@ -0,0 +1,66 @@ +package com.databricks.jdbc.integration.e2e; + +import static com.databricks.jdbc.integration.IntegrationTestUtil.*; +import static org.junit.jupiter.api.Assertions.*; + +import java.sql.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ParameterBindTypeTests { + + private Connection connection; + + @BeforeEach + void setUp() throws SQLException { + connection = getValidJDBCConnection(); + } + + @AfterEach + void cleanUp() throws SQLException { + if (connection != null) { + connection.close(); + } + } + + /** + * PARAMQUERY-021: the declared SQL target type — not the bound value's native type — must drive + * the wire type. Per the JDBC spec (Appendix B), Types.FLOAT is a synonym for DOUBLE (8-byte + * double precision) while Types.REAL is 4-byte single precision. Binding the same textual value + * against Types.FLOAT vs Types.REAL must therefore yield differently-typed result columns for a + * bare marker projection. + */ + @Test + void testTargetTypeDrivesWireTypeAcrossScalarTypes() throws SQLException { + double value = 1234567.89012345d; + + // Types.FLOAT must ride as the 8-byte DOUBLE per JDBC spec. + try (PreparedStatement stmt = connection.prepareStatement("SELECT ? AS v")) { + stmt.setObject(1, value, Types.FLOAT); + try (ResultSet rs = stmt.executeQuery()) { + assertTrue(rs.next(), "expected exactly 1 row"); + assertEquals( + Types.DOUBLE, + rs.getMetaData().getColumnType(1), + "Types.FLOAT bind must ride as the 8-byte DOUBLE per JDBC spec Appendix B"); + assertEquals( + value, rs.getDouble(1), 0.0d, "DOUBLE bind must round-trip at double precision"); + assertFalse(rs.next(), "expected exactly 1 row"); + } + } + + // Types.REAL must ride as the 4-byte FLOAT, distinct from Types.FLOAT. + try (PreparedStatement stmt = connection.prepareStatement("SELECT ? AS v")) { + stmt.setObject(1, value, Types.REAL); + try (ResultSet rs = stmt.executeQuery()) { + assertTrue(rs.next(), "expected exactly 1 row"); + assertEquals( + Types.FLOAT, + rs.getMetaData().getColumnType(1), + "Types.REAL bind must ride as the 4-byte FLOAT per JDBC spec Appendix B"); + assertFalse(rs.next(), "expected exactly 1 row"); + } + } + } +}