Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stored procedure call returns wrong BigDecimal scale and integrated Mockito Dependency into JDBC Driver Codebase. #2582

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -150,6 +151,22 @@
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);

Check warning on line 166 in src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java#L164-L166

Added lines #L164 - L166 were not covered by tests
}
}
break;
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")));
}
}
}
Expand Down Expand Up @@ -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);
}
}
Ananya2 marked this conversation as resolved.
Show resolved Hide resolved
}

@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");
}
}
}
}
}
Loading