From 6485c7778b4884d49077ca5ae559a11f29d48195 Mon Sep 17 00:00:00 2001 From: Ananya Garg Date: Mon, 13 Jan 2025 10:28:26 +0530 Subject: [PATCH 1/6] Stored procedure call returns wrong BigDecimal scale. --- .../jdbc/SQLServerCallableStatement.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java index e6e42ada6..8e40c2fca 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java @@ -23,6 +23,7 @@ import java.sql.SQLXML; import java.sql.Time; import java.sql.Timestamp; +import java.sql.ParameterMetaData; import java.text.MessageFormat; import java.time.LocalDateTime; import java.util.Calendar; @@ -150,6 +151,22 @@ public void registerOutParameter(int index, int sqlType) throws SQLServerExcepti case microsoft.sql.Types.DATETIMEOFFSET: param.setOutScale(7); break; + case java.sql.Types.DECIMAL: + // Dynamically handle the scale for DECIMAL output parameters. + // The scale for the DECIMAL type is fetched from the ParameterMetaData. + // This provides flexibility to automatically apply the correct scale as per the database metadata. + ParameterMetaData parameterMetaData = this.getParameterMetaData(); + if (parameterMetaData != null) { + try { + // Fetch scale from metadata for DECIMAL type + int scale = parameterMetaData.getScale(index); + param.setOutScale(scale); + } catch (SQLException e) { + loggerExternal.warning("Failed to fetch scale for DECIMAL type parameter at index " + index + ": " + e.getMessage()); + throw new SQLServerException(SQLServerException.getErrString("R_InvalidScale"), null, 0, e); + } + } + break; default: break; } From aa1ce7b8dc4b47692e5ed248f2c4a085f75fa59a Mon Sep 17 00:00:00 2001 From: Ananya Garg Date: Mon, 13 Jan 2025 10:30:06 +0530 Subject: [PATCH 2/6] Added test case to validate the BigDecimal precision. --- .../jdbc/unit/statement/StatementTest.java | 117 +++++++++++++++++- 1 file changed, 111 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java index 9c814916d..e1b8f0627 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java @@ -1195,15 +1195,14 @@ public void testJdbc41CallableStatementMethods() throws Exception { assertEquals("2017-05-19 10:47:15.1234567 +02:00", cstmt.getObject("col14Value", microsoft.sql.DateTimeOffset.class).toString()); - // BigDecimal#equals considers the number of decimal places (OutParams always return 4 decimal - // digits rounded up) - assertEquals(0, cstmt.getObject(15, BigDecimal.class).compareTo(new BigDecimal("0.1235"))); + // BigDecimal#equals considers the number of decimal places (OutParams always return full precision as specified in the DB schema) + assertEquals(0, cstmt.getObject(15, BigDecimal.class).compareTo(new BigDecimal("0.123456789"))); assertEquals(0, - cstmt.getObject("col15Value", BigDecimal.class).compareTo(new BigDecimal("0.1235"))); + cstmt.getObject("col15Value", BigDecimal.class).compareTo(new BigDecimal("0.123456789"))); - assertEquals(0, cstmt.getObject(16, BigDecimal.class).compareTo(new BigDecimal("0.1235"))); + assertEquals(0, cstmt.getObject(16, BigDecimal.class).compareTo(new BigDecimal("0.1234567890123456789012345678901234567"))); assertEquals(0, - cstmt.getObject("col16Value", BigDecimal.class).compareTo(new BigDecimal("0.1235"))); + cstmt.getObject("col16Value", BigDecimal.class).compareTo(new BigDecimal("0.1234567890123456789012345678901234567"))); } } } @@ -2692,4 +2691,110 @@ public void terminate() throws Exception { } } } + + @Nested + @Tag(Constants.xAzureSQLDW) + public class BigDecimalPrecisionTest { + + private final String procName1 = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_3")); + private final String procName2 = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_5")); + private final String procNameMaxScale = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_max_scale")); + private final String procNameMaxPrecision = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_max_precision")); + + @BeforeEach + public void init() throws SQLException { + try (Connection connection = getConnection()) { + String dropProcedureSQL = "DROP PROCEDURE IF EXISTS " + procName1 + ", " + procName2 + ", " + + procNameMaxScale + ", " + procNameMaxPrecision; + try (Statement stmt = connection.createStatement()) { + stmt.execute(dropProcedureSQL); + } + + String createProcedureSQL1 = "CREATE PROCEDURE " + procName1 + "\n" + + " @big_decimal_type decimal(15, 3),\n" + + " @big_decimal_type_o decimal(15, 3) OUTPUT\n" + "AS\n" + "BEGIN\n" + + " SET @big_decimal_type_o = @big_decimal_type;\n" + "END;"; + String createProcedureSQL2 = "CREATE PROCEDURE " + procName2 + "\n" + + " @big_decimal_type decimal(15, 5),\n" + + " @big_decimal_type_o decimal(15, 5) OUTPUT\n" + "AS\n" + "BEGIN\n" + + " SET @big_decimal_type_o = @big_decimal_type;\n" + "END;"; + String createProcedureMaxScale = "CREATE PROCEDURE " + procNameMaxScale + "\n" + + " @big_decimal_type decimal(38, 38),\n" + + " @big_decimal_type_o decimal(38, 38) OUTPUT\n" + "AS\n" + "BEGIN\n" + + " SET @big_decimal_type_o = @big_decimal_type;\n" + "END;"; + String createProcedureMaxPrecision = "CREATE PROCEDURE " + procNameMaxPrecision + "\n" + + " @big_decimal_type decimal(38, 0),\n" + + " @big_decimal_type_o decimal(38, 0) OUTPUT\n" + "AS\n" + "BEGIN\n" + + " SET @big_decimal_type_o = @big_decimal_type;\n" + "END;"; + + try (Statement stmt = connection.createStatement()) { + stmt.execute(createProcedureSQL1); + stmt.execute(createProcedureSQL2); + stmt.execute(createProcedureMaxScale); + stmt.execute(createProcedureMaxPrecision); + } + } + } + + @AfterEach + public void terminate() throws SQLException { + try (Connection connection = getConnection()) { + try (Statement stmt = connection.createStatement()) { + String dropProcedureSQL = "DROP PROCEDURE IF EXISTS " + procName1 + ", " + procName2 + ", " + + procNameMaxScale + ", " + procNameMaxPrecision; + stmt.execute(dropProcedureSQL); + } + } + } + + @Test + @Tag("BigDecimal") + public void testBigDecimalPrecision() throws SQLException { + try (Connection connection = getConnection()) { + // Test for DECIMAL(15, 3) + String callSQL1 = "{call " + procName1 + "(100.241, ?)}"; + try (CallableStatement call = connection.prepareCall(callSQL1)) { + call.registerOutParameter(1, Types.DECIMAL); + call.execute(); + BigDecimal actual1 = call.getBigDecimal(1); + assertEquals(new BigDecimal("100.241"), actual1); + } + + // Test for DECIMAL(15, 5) + String callSQL2 = "{call " + procName2 + "(100.24112, ?)}"; + try (CallableStatement call = connection.prepareCall(callSQL2)) { + call.registerOutParameter(1, Types.DECIMAL); + call.execute(); + BigDecimal actual2 = call.getBigDecimal(1); + assertEquals(new BigDecimal("100.24112"), actual2); + } + + // Test for DECIMAL(38, 38) + String callSQLMaxScale = "{call " + procNameMaxScale + "(?, ?)}"; + try (CallableStatement call = connection.prepareCall(callSQLMaxScale)) { + BigDecimal maxScaleValue = new BigDecimal("0." + "1".repeat(38)); + call.setBigDecimal(1, maxScaleValue); + call.registerOutParameter(2, Types.DECIMAL); + call.execute(); + BigDecimal actualMaxScale = call.getBigDecimal(2); + assertEquals(maxScaleValue, actualMaxScale, "DECIMAL(38, 38) max scale test failed"); + } + + // Test for DECIMAL(38, 0) + String callSQLMaxPrecision = "{call " + procNameMaxPrecision + "(?, ?)}"; + try (CallableStatement call = connection.prepareCall(callSQLMaxPrecision)) { + BigDecimal maxPrecisionValue = new BigDecimal("9".repeat(38)); + call.setBigDecimal(1, maxPrecisionValue); + call.registerOutParameter(2, Types.DECIMAL); + call.execute(); + BigDecimal actualMaxPrecision = call.getBigDecimal(2); + assertEquals(maxPrecisionValue, actualMaxPrecision, "DECIMAL(38, 0) max precision test failed"); + } + } + } + } } From 2f3ea6359a88ae3b0321027d69a8fcc36f1753d0 Mon Sep 17 00:00:00 2001 From: Ananya Garg Date: Fri, 17 Jan 2025 12:01:01 +0530 Subject: [PATCH 3/6] Removed repeat method since it is not compatible with JDK 8. --- .../sqlserver/jdbc/unit/statement/StatementTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java index e1b8f0627..d7b3ec57f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java @@ -2776,7 +2776,7 @@ public void testBigDecimalPrecision() throws SQLException { // Test for DECIMAL(38, 38) String callSQLMaxScale = "{call " + procNameMaxScale + "(?, ?)}"; try (CallableStatement call = connection.prepareCall(callSQLMaxScale)) { - BigDecimal maxScaleValue = new BigDecimal("0." + "1".repeat(38)); + BigDecimal maxScaleValue = new BigDecimal("0.11111111111111111111111111111111111111"); call.setBigDecimal(1, maxScaleValue); call.registerOutParameter(2, Types.DECIMAL); call.execute(); @@ -2787,7 +2787,7 @@ public void testBigDecimalPrecision() throws SQLException { // Test for DECIMAL(38, 0) String callSQLMaxPrecision = "{call " + procNameMaxPrecision + "(?, ?)}"; try (CallableStatement call = connection.prepareCall(callSQLMaxPrecision)) { - BigDecimal maxPrecisionValue = new BigDecimal("9".repeat(38)); + BigDecimal maxPrecisionValue = new BigDecimal("99999999999999999999999999999999999999"); call.setBigDecimal(1, maxPrecisionValue); call.registerOutParameter(2, Types.DECIMAL); call.execute(); From ee65c4b234439a95be4c82be8ca62185bbd2ed51 Mon Sep 17 00:00:00 2001 From: Ananya Garg Date: Mon, 27 Jan 2025 10:59:34 +0530 Subject: [PATCH 4/6] updated query to TestUtils.dropProcedureIfExists() --- .../sqlserver/jdbc/unit/statement/StatementTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java index d7b3ec57f..f05570a36 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java @@ -2708,10 +2708,11 @@ public class BigDecimalPrecisionTest { @BeforeEach public void init() throws SQLException { try (Connection connection = getConnection()) { - String dropProcedureSQL = "DROP PROCEDURE IF EXISTS " + procName1 + ", " + procName2 + ", " - + procNameMaxScale + ", " + procNameMaxPrecision; try (Statement stmt = connection.createStatement()) { - stmt.execute(dropProcedureSQL); + TestUtils.dropProcedureIfExists(procName1, stmt); + TestUtils.dropProcedureIfExists(procName2, stmt); + TestUtils.dropProcedureIfExists(procNameMaxScale, stmt); + TestUtils.dropProcedureIfExists(procNameMaxPrecision, stmt); } String createProcedureSQL1 = "CREATE PROCEDURE " + procName1 + "\n" From e827f0ac815b2e96f82529e110ad141d2f03af19 Mon Sep 17 00:00:00 2001 From: Ananya Garg Date: Tue, 28 Jan 2025 14:27:01 +0530 Subject: [PATCH 5/6] Integrated Mockito Dependency into JDBC Driver Codebase and added a test case to validate registerOutParameter method catch block for BigDecimal. --- build.gradle | 5 +- pom.xml | 17 ++ .../jdbc/unit/statement/StatementTest.java | 239 ++++++++++-------- 3 files changed, 159 insertions(+), 102 deletions(-) diff --git a/build.gradle b/build.gradle index 3e5d87a9e..febc46eb1 100644 --- a/build.gradle +++ b/build.gradle @@ -167,5 +167,8 @@ dependencies { 'org.bouncycastle:bcprov-jdk18on:1.78', 'com.azure:azure-security-keyvault-keys:4.7.3', 'com.azure:azure-identity:1.12.2', - 'com.h2database:h2:2.2.220' + 'com.h2database:h2:2.2.220', + 'org.mockito:mockito-core:5.15.2', + 'net.bytebuddy:byte-buddy:1.15.11', + 'net.bytebuddy:byte-buddy-agent:1.15.11' } diff --git a/pom.xml b/pom.xml index 026144f46..6b2c4937d 100644 --- a/pom.xml +++ b/pom.xml @@ -140,6 +140,23 @@ provided + + org.mockito + mockito-core + 5.15.2 + test + + + net.bytebuddy + byte-buddy + 1.15.11 + + + net.bytebuddy + byte-buddy-agent + 1.15.11 + test + org.junit.platform junit-platform-console diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java index f05570a36..fa8904757 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java @@ -7,7 +7,11 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.fail; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.spy; import java.io.StringReader; import java.math.BigDecimal; @@ -17,6 +21,7 @@ import java.sql.Clob; import java.sql.Connection; import java.sql.NClob; +import java.sql.ParameterMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -2696,106 +2701,138 @@ public void terminate() throws Exception { @Tag(Constants.xAzureSQLDW) public class BigDecimalPrecisionTest { - private final String procName1 = AbstractSQLGenerator - .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_3")); - private final String procName2 = AbstractSQLGenerator - .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_5")); - private final String procNameMaxScale = AbstractSQLGenerator - .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_max_scale")); - private final String procNameMaxPrecision = AbstractSQLGenerator - .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_max_precision")); - - @BeforeEach - public void init() throws SQLException { - try (Connection connection = getConnection()) { - try (Statement stmt = connection.createStatement()) { - TestUtils.dropProcedureIfExists(procName1, stmt); - TestUtils.dropProcedureIfExists(procName2, stmt); - TestUtils.dropProcedureIfExists(procNameMaxScale, stmt); - TestUtils.dropProcedureIfExists(procNameMaxPrecision, stmt); - } - - String createProcedureSQL1 = "CREATE PROCEDURE " + procName1 + "\n" - + " @big_decimal_type decimal(15, 3),\n" - + " @big_decimal_type_o decimal(15, 3) OUTPUT\n" + "AS\n" + "BEGIN\n" - + " SET @big_decimal_type_o = @big_decimal_type;\n" + "END;"; - String createProcedureSQL2 = "CREATE PROCEDURE " + procName2 + "\n" - + " @big_decimal_type decimal(15, 5),\n" - + " @big_decimal_type_o decimal(15, 5) OUTPUT\n" + "AS\n" + "BEGIN\n" - + " SET @big_decimal_type_o = @big_decimal_type;\n" + "END;"; - String createProcedureMaxScale = "CREATE PROCEDURE " + procNameMaxScale + "\n" - + " @big_decimal_type decimal(38, 38),\n" - + " @big_decimal_type_o decimal(38, 38) OUTPUT\n" + "AS\n" + "BEGIN\n" - + " SET @big_decimal_type_o = @big_decimal_type;\n" + "END;"; - String createProcedureMaxPrecision = "CREATE PROCEDURE " + procNameMaxPrecision + "\n" - + " @big_decimal_type decimal(38, 0),\n" - + " @big_decimal_type_o decimal(38, 0) OUTPUT\n" + "AS\n" + "BEGIN\n" - + " SET @big_decimal_type_o = @big_decimal_type;\n" + "END;"; - - try (Statement stmt = connection.createStatement()) { - stmt.execute(createProcedureSQL1); - stmt.execute(createProcedureSQL2); - stmt.execute(createProcedureMaxScale); - stmt.execute(createProcedureMaxPrecision); - } - } - } - - @AfterEach - public void terminate() throws SQLException { - try (Connection connection = getConnection()) { - try (Statement stmt = connection.createStatement()) { - String dropProcedureSQL = "DROP PROCEDURE IF EXISTS " + procName1 + ", " + procName2 + ", " - + procNameMaxScale + ", " + procNameMaxPrecision; - stmt.execute(dropProcedureSQL); - } - } - } - - @Test - @Tag("BigDecimal") - public void testBigDecimalPrecision() throws SQLException { - try (Connection connection = getConnection()) { - // Test for DECIMAL(15, 3) - String callSQL1 = "{call " + procName1 + "(100.241, ?)}"; - try (CallableStatement call = connection.prepareCall(callSQL1)) { - call.registerOutParameter(1, Types.DECIMAL); - call.execute(); - BigDecimal actual1 = call.getBigDecimal(1); - assertEquals(new BigDecimal("100.241"), actual1); - } - - // Test for DECIMAL(15, 5) - String callSQL2 = "{call " + procName2 + "(100.24112, ?)}"; - try (CallableStatement call = connection.prepareCall(callSQL2)) { - call.registerOutParameter(1, Types.DECIMAL); - call.execute(); - BigDecimal actual2 = call.getBigDecimal(1); - assertEquals(new BigDecimal("100.24112"), actual2); - } - - // Test for DECIMAL(38, 38) - String callSQLMaxScale = "{call " + procNameMaxScale + "(?, ?)}"; - try (CallableStatement call = connection.prepareCall(callSQLMaxScale)) { - BigDecimal maxScaleValue = new BigDecimal("0.11111111111111111111111111111111111111"); - call.setBigDecimal(1, maxScaleValue); - call.registerOutParameter(2, Types.DECIMAL); - call.execute(); - BigDecimal actualMaxScale = call.getBigDecimal(2); - assertEquals(maxScaleValue, actualMaxScale, "DECIMAL(38, 38) max scale test failed"); - } - - // Test for DECIMAL(38, 0) - String callSQLMaxPrecision = "{call " + procNameMaxPrecision + "(?, ?)}"; - try (CallableStatement call = connection.prepareCall(callSQLMaxPrecision)) { - BigDecimal maxPrecisionValue = new BigDecimal("99999999999999999999999999999999999999"); - call.setBigDecimal(1, maxPrecisionValue); - call.registerOutParameter(2, Types.DECIMAL); - call.execute(); - BigDecimal actualMaxPrecision = call.getBigDecimal(2); - assertEquals(maxPrecisionValue, actualMaxPrecision, "DECIMAL(38, 0) max precision test failed"); - } - } - } + private final String procName1 = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_3")); + private final String procName2 = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_5")); + private final String procNameMaxScale = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_max_scale")); + private final String procNameMaxPrecision = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_max_precision")); + private final String procNameForCatchBlock = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("test_bigdecimal_catch_block")); + + @BeforeEach + public void init() throws SQLException { + try (Connection connection = getConnection()) { + try (Statement stmt = connection.createStatement()) { + TestUtils.dropProcedureIfExists(procName1, stmt); + TestUtils.dropProcedureIfExists(procName2, stmt); + TestUtils.dropProcedureIfExists(procNameMaxScale, stmt); + TestUtils.dropProcedureIfExists(procNameMaxPrecision, stmt); + TestUtils.dropProcedureIfExists(procNameForCatchBlock, stmt); + } + + String createProcedureSQL1 = "CREATE PROCEDURE " + procName1 + "\n" + + " @big_decimal_type decimal(15, 3),\n" + + " @big_decimal_type_o decimal(15, 3) OUTPUT\n" + "AS\n" + "BEGIN\n" + + " SET @big_decimal_type_o = @big_decimal_type;\n" + "END;"; + String createProcedureSQL2 = "CREATE PROCEDURE " + procName2 + "\n" + + " @big_decimal_type decimal(15, 5),\n" + + " @big_decimal_type_o decimal(15, 5) OUTPUT\n" + "AS\n" + "BEGIN\n" + + " SET @big_decimal_type_o = @big_decimal_type;\n" + "END;"; + String createProcedureMaxScale = "CREATE PROCEDURE " + procNameMaxScale + "\n" + + " @big_decimal_type decimal(38, 38),\n" + + " @big_decimal_type_o decimal(38, 38) OUTPUT\n" + "AS\n" + "BEGIN\n" + + " SET @big_decimal_type_o = @big_decimal_type;\n" + "END;"; + String createProcedureMaxPrecision = "CREATE PROCEDURE " + procNameMaxPrecision + "\n" + + " @big_decimal_type decimal(38, 0),\n" + + " @big_decimal_type_o decimal(38, 0) OUTPUT\n" + "AS\n" + "BEGIN\n" + + " SET @big_decimal_type_o = @big_decimal_type;\n" + "END;"; + String createProcedureForCatchBlock = "CREATE PROCEDURE " + procNameForCatchBlock + "\n" + + "@outParam DECIMAL(10,2) OUTPUT " + + "AS BEGIN SET @outParam = 123.45 END"; + + try (Statement stmt = connection.createStatement()) { + stmt.execute(createProcedureSQL1); + stmt.execute(createProcedureSQL2); + stmt.execute(createProcedureMaxScale); + stmt.execute(createProcedureMaxPrecision); + stmt.execute(createProcedureForCatchBlock); + } + } + } + + @AfterEach + public void terminate() throws SQLException { + try (Connection connection = getConnection(); Statement stmt = connection.createStatement()) { + TestUtils.dropProcedureIfExists(procName1, stmt); + TestUtils.dropProcedureIfExists(procName2, stmt); + TestUtils.dropProcedureIfExists(procNameMaxScale, stmt); + TestUtils.dropProcedureIfExists(procNameMaxPrecision, stmt); + TestUtils.dropProcedureIfExists(procNameForCatchBlock, stmt); + } + } + + @Test + @Tag("BigDecimal") + public void testBigDecimalPrecision() throws SQLException { + try (Connection connection = getConnection()) { + // Test for DECIMAL(15, 3) + String callSQL1 = "{call " + procName1 + "(100.241, ?)}"; + try (CallableStatement call = connection.prepareCall(callSQL1)) { + call.registerOutParameter(1, Types.DECIMAL); + call.execute(); + BigDecimal actual1 = call.getBigDecimal(1); + assertEquals(new BigDecimal("100.241"), actual1); + } + + // Test for DECIMAL(15, 5) + String callSQL2 = "{call " + procName2 + "(100.24112, ?)}"; + try (CallableStatement call = connection.prepareCall(callSQL2)) { + call.registerOutParameter(1, Types.DECIMAL); + call.execute(); + BigDecimal actual2 = call.getBigDecimal(1); + assertEquals(new BigDecimal("100.24112"), actual2); + } + + // Test for DECIMAL(38, 38) + String callSQLMaxScale = "{call " + procNameMaxScale + "(?, ?)}"; + try (CallableStatement call = connection.prepareCall(callSQLMaxScale)) { + BigDecimal maxScaleValue = new BigDecimal("0.11111111111111111111111111111111111111"); + call.setBigDecimal(1, maxScaleValue); + call.registerOutParameter(2, Types.DECIMAL); + call.execute(); + BigDecimal actualMaxScale = call.getBigDecimal(2); + assertEquals(maxScaleValue, actualMaxScale, "DECIMAL(38, 38) max scale test failed"); + } + + // Test for DECIMAL(38, 0) + String callSQLMaxPrecision = "{call " + procNameMaxPrecision + "(?, ?)}"; + try (CallableStatement call = connection.prepareCall(callSQLMaxPrecision)) { + BigDecimal maxPrecisionValue = new BigDecimal("99999999999999999999999999999999999999"); + call.setBigDecimal(1, maxPrecisionValue); + call.registerOutParameter(2, Types.DECIMAL); + call.execute(); + BigDecimal actualMaxPrecision = call.getBigDecimal(2); + assertEquals(maxPrecisionValue, actualMaxPrecision, "DECIMAL(38, 0) max precision test failed"); + } + } + } + + @Test + @Tag("BigDecimal") + public void testRegisterOutParameterForBigDecimalCatchBlock() throws SQLException { + try (Connection con = getConnection()) { + try (CallableStatement realCallableStatement = con.prepareCall("{call "+ procNameForCatchBlock + "(?)}")) { + + CallableStatement spyCallableStatement = spy(realCallableStatement); + ParameterMetaData spyMetaData = spy(realCallableStatement.getParameterMetaData()); + + // Simulate an exception for getScale + doThrow(new SQLException("Simulated error for BigDecimal scale retrieval")) + .when(spyMetaData).getScale(1); + + // Inject the mocked ParameterMetaData back into the spy CallableStatement + doReturn(spyMetaData).when(spyCallableStatement).getParameterMetaData(); + + // Assert that the catch block for SQLException is executed + SQLServerException thrownException = assertThrows(SQLServerException.class, () -> { + spyCallableStatement.registerOutParameter(1, java.sql.Types.DECIMAL); + }); + assertTrue(thrownException.getMessage().matches(TestUtils.formatErrorMsg("R_InvalidScale")), thrownException.getMessage()); + } + } + } } } From 43c9d99eecba5d681fefed81fa445b4cf1d59981 Mon Sep 17 00:00:00 2001 From: Ananya Garg Date: Thu, 30 Jan 2025 10:42:25 +0530 Subject: [PATCH 6/6] Updated Mockito-core to 4.11.0 to ensure compatibility with JDK 8 and higher versions. --- build.gradle | 3 ++- pom.xml | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index febc46eb1..f40fa3be1 100644 --- a/build.gradle +++ b/build.gradle @@ -168,7 +168,8 @@ dependencies { 'com.azure:azure-security-keyvault-keys:4.7.3', 'com.azure:azure-identity:1.12.2', 'com.h2database:h2:2.2.220', - 'org.mockito:mockito-core:5.15.2', + 'org.mockito:mockito-core:4.11.0', + 'org.mockito:mockito-inline:4.11.0', 'net.bytebuddy:byte-buddy:1.15.11', 'net.bytebuddy:byte-buddy-agent:1.15.11' } diff --git a/pom.xml b/pom.xml index 6b2c4937d..57b88810b 100644 --- a/pom.xml +++ b/pom.xml @@ -143,7 +143,13 @@ org.mockito mockito-core - 5.15.2 + 4.11.0 + test + + + org.mockito + mockito-inline + 4.11.0 test