diff --git a/agents/drivers/cache/src/main/java/com/dbx/agent/cache/CacheAgent.java b/agents/drivers/cache/src/main/java/com/dbx/agent/cache/CacheAgent.java index d8915fa19f..d13b931414 100644 --- a/agents/drivers/cache/src/main/java/com/dbx/agent/cache/CacheAgent.java +++ b/agents/drivers/cache/src/main/java/com/dbx/agent/cache/CacheAgent.java @@ -42,10 +42,22 @@ public final class CacheAgent extends ConfiguredJdbcAgent { @Override protected Object resultValue(ResultSet rs, int index, int sqlType) { - if (sqlType == Types.OTHER) { - return unchecked(() -> JdbcExecutor.normalizeResultValue(rs.getObject(index))); + switch (sqlType) { + case Types.BIGINT: + case Types.INTEGER: + case Types.SMALLINT: + case Types.TINYINT: + // Caché can expose values outside the Java primitive range + // implied by its JDBC metadata. Preserve them without narrowing. + return unchecked(() -> { + Object value = rs.getBigDecimal(index); + return rs.wasNull() ? null : value; + }); + case Types.OTHER: + return unchecked(() -> JdbcExecutor.normalizeResultValue(rs.getObject(index))); + default: + return super.resultValue(rs, index, sqlType); } - return super.resultValue(rs, index, sqlType); } @Override diff --git a/agents/drivers/cache/src/test/java/com/dbx/agent/cache/CacheAgentTest.java b/agents/drivers/cache/src/test/java/com/dbx/agent/cache/CacheAgentTest.java index 235a23d26f..e8d739c086 100644 --- a/agents/drivers/cache/src/test/java/com/dbx/agent/cache/CacheAgentTest.java +++ b/agents/drivers/cache/src/test/java/com/dbx/agent/cache/CacheAgentTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.math.BigDecimal; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; @@ -88,8 +89,38 @@ class CacheAgentTest { assertEquals(Collections.singletonList("getObject"), calls); } + @Test + void readsOutOfRangeIntegerThroughBigDecimal() { + BigDecimal valueOutsideIntegerRange = new BigDecimal("2147483648"); + List calls = new ArrayList<>(); + ResultSet resultSet = proxy(ResultSet.class, (method, args) -> { + if ("getBigDecimal".equals(method.getName())) { + calls.add("getBigDecimal"); + return valueOutsideIntegerRange; + } + if (method.getName().startsWith("get")) { + calls.add(method.getName()); + throw new SQLException("Numeric value out of range"); + } + if ("wasNull".equals(method.getName())) { + return false; + } + return defaultValue(method.getReturnType()); + }); + + Object value = new CacheAgent().resultValue(resultSet, 1, Types.INTEGER); + + assertEquals(valueOutsideIntegerRange, value); + assertEquals(Collections.singletonList("getBigDecimal"), calls); + } + @Test void preservesStringPathForStandardValues() { + assertStringPath(Types.VARCHAR); + assertStringPath(Types.LONGVARCHAR); + } + + private static void assertStringPath(int sqlType) { List calls = new ArrayList<>(); ResultSet resultSet = proxy(ResultSet.class, (method, args) -> { if ("getString".equals(method.getName())) { @@ -102,7 +133,7 @@ class CacheAgentTest { return defaultValue(method.getReturnType()); }); - Object value = new CacheAgent().resultValue(resultSet, 1, Types.VARCHAR); + Object value = new CacheAgent().resultValue(resultSet, 1, sqlType); assertEquals("ordinary", value); assertEquals(Collections.singletonList("getString"), calls); diff --git a/agents/drivers/iris/src/main/java/com/dbx/agent/iris/IrisAgent.java b/agents/drivers/iris/src/main/java/com/dbx/agent/iris/IrisAgent.java index 155790ccc5..fbc07fc6bd 100644 --- a/agents/drivers/iris/src/main/java/com/dbx/agent/iris/IrisAgent.java +++ b/agents/drivers/iris/src/main/java/com/dbx/agent/iris/IrisAgent.java @@ -67,10 +67,22 @@ public final class IrisAgent extends ConfiguredJdbcAgent { @Override protected Object resultValue(ResultSet rs, int index, int sqlType) { - if (sqlType == Types.OTHER) { - return unchecked(() -> JdbcExecutor.normalizeResultValue(rs.getObject(index))); + switch (sqlType) { + case Types.BIGINT: + case Types.INTEGER: + case Types.SMALLINT: + case Types.TINYINT: + // InterSystems can expose values outside the Java primitive range + // implied by its JDBC metadata. Preserve them without narrowing. + return unchecked(() -> { + Object value = rs.getBigDecimal(index); + return rs.wasNull() ? null : value; + }); + case Types.OTHER: + return unchecked(() -> JdbcExecutor.normalizeResultValue(rs.getObject(index))); + default: + return super.resultValue(rs, index, sqlType); } - return super.resultValue(rs, index, sqlType); } @Override diff --git a/agents/drivers/iris/src/test/java/com/dbx/agent/iris/IrisAgentTest.java b/agents/drivers/iris/src/test/java/com/dbx/agent/iris/IrisAgentTest.java index 9114eb6204..98ac28d883 100644 --- a/agents/drivers/iris/src/test/java/com/dbx/agent/iris/IrisAgentTest.java +++ b/agents/drivers/iris/src/test/java/com/dbx/agent/iris/IrisAgentTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.math.BigDecimal; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; @@ -76,8 +77,38 @@ class IrisAgentTest { assertEquals(Collections.singletonList("getObject"), calls); } + @Test + void readsOutOfRangeIntegerThroughBigDecimal() { + BigDecimal valueOutsideIntegerRange = new BigDecimal("2147483648"); + List calls = new ArrayList<>(); + ResultSet resultSet = proxy(ResultSet.class, (method, args) -> { + if ("getBigDecimal".equals(method.getName())) { + calls.add("getBigDecimal"); + return valueOutsideIntegerRange; + } + if (method.getName().startsWith("get")) { + calls.add(method.getName()); + throw new SQLException("Numeric value out of range"); + } + if ("wasNull".equals(method.getName())) { + return false; + } + return defaultValue(method.getReturnType()); + }); + + Object value = new IrisAgent().resultValue(resultSet, 1, Types.INTEGER); + + assertEquals(valueOutsideIntegerRange, value); + assertEquals(Collections.singletonList("getBigDecimal"), calls); + } + @Test void preservesStringPathForStandardValues() { + assertStringPath(Types.VARCHAR); + assertStringPath(Types.LONGVARCHAR); + } + + private static void assertStringPath(int sqlType) { List calls = new ArrayList<>(); ResultSet resultSet = proxy(ResultSet.class, (method, args) -> { if ("getString".equals(method.getName())) { @@ -90,7 +121,7 @@ class IrisAgentTest { return defaultValue(method.getReturnType()); }); - Object value = new IrisAgent().resultValue(resultSet, 1, Types.VARCHAR); + Object value = new IrisAgent().resultValue(resultSet, 1, sqlType); assertEquals("ordinary", value); assertEquals(Collections.singletonList("getString"), calls);