Skip to content

Commit

Permalink
Check for maximum scale in IOBuffer before array copy (#2239)
Browse files Browse the repository at this point in the history
* Simple check to see if the input exceeds scale of 38.

* Added test

* Update failing tests

* Cleanup

* More cleanup

* Move to TVPTypesTest

* The scale check was somehow failing coco, instead moved to just catch the IndexOutOfBoundsException

* ArrayIndexOutOfBoundsException not IndexOutOfBoundsException

* More general

* Revert

* This should have been rolled back in the retry PR

* This test will be removed in a separate PR

* ...

* Changed the error message

* Forgot to change the test as well

* Better error
  • Loading branch information
Jeffery-Wasty authored Nov 3, 2023
1 parent a762d7c commit 729520d
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 3 deletions.
14 changes: 12 additions & 2 deletions src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -5028,7 +5028,8 @@ void writeTVPRows(TVP value) throws SQLServerException {
}

private void writeInternalTVPRowValues(JDBCType jdbcType, String currentColumnStringValue, Object currentObject,
Map.Entry<Integer, SQLServerMetaData> columnPair, boolean isSqlVariant) throws SQLServerException {
Map.Entry<Integer, SQLServerMetaData> columnPair, boolean isSqlVariant)
throws SQLServerException, IllegalArgumentException {
boolean isShortValue, isNull;
int dataLength;
switch (jdbcType) {
Expand Down Expand Up @@ -5100,9 +5101,18 @@ private void writeInternalTVPRowValues(JDBCType jdbcType, String currentColumnSt

/*
* setScale of all BigDecimal value based on metadata as scale is not sent separately for individual
* value. Use the rounding used in Server. Say, for BigDecimal("0.1"), if scale in metdadata is 0,
* value. Use the rounding used in Server. Say, for BigDecimal("0.1"), if scale in metadata is 0,
* then ArithmeticException would be thrown if RoundingMode is not set
*
* Additionally, we should check here if the scale is within the bounds of SQLServer as it is
* possible for a number with a scale larger than 38 to be passed in.
*/
if (columnPair.getValue().scale > SQLServerConnection.MAX_DECIMAL_PRECISION) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_InvalidScale"));
Object[] msgArgs = {columnPair.getValue().scale};
throw new IllegalArgumentException(form.format(msgArgs));
}

bdValue = bdValue.setScale(columnPair.getValue().scale, RoundingMode.HALF_UP);

byte[] val = DDC.convertBigDecimalToBytes(bdValue, bdValue.scale());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,8 @@ protected Object[][] getContents() {
{"R_serverError", "An error occurred during the current command (Done status {0}). {1}"},
{"R_ManagedIdentityTokenAcquisitionFail", "Failed to acquire managed identity token. Request for the token succeeded, but no token was returned. The token is null."},
{"R_AmbiguousRowUpdate", "Failed to execute updateRow(). The update is attempting an ambiguous update on tables \"{0}\" and \"{1}\". Ensure all columns being updated prior to the updateRow() call belong to the same table."},
{"R_InvalidSqlQuery", "Invalid SQL Query: {0}"}
{"R_InvalidSqlQuery", "Invalid SQL Query: {0}"},
{"R_InvalidScale", "Scale of input value is larger than the maximum allowed by SQL Server."}
};
}
// @formatter:on
28 changes: 28 additions & 0 deletions src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPTypesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
Expand Down Expand Up @@ -573,6 +574,33 @@ public String toString() {
}
}

/**
* Numeric (bigdecimal) with StoredProcedure
*
* @throws SQLException
*/
@Test
public void testTVPNumericStoredProcedure() throws SQLException {
createTables("numeric(10,2)");
createTVPS("numeric(38,10)");
createProcedure();

tvp = new SQLServerDataTable();
tvp.addColumnMetadata("c1", java.sql.Types.NUMERIC);
tvp.addRow(new BigDecimal(0.222));

final String sql = "{call " + AbstractSQLGenerator.escapeIdentifier(procedureName) + "(?)}";

try (SQLServerCallableStatement callableStmt = (SQLServerCallableStatement) connection.prepareCall(sql)) {
callableStmt.setStructured(1, tvpName, tvp);
callableStmt.execute();

fail(TestResource.getResource("R_expectedExceptionNotThrown"));
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().matches(TestUtils.formatErrorMsg("R_InvalidScale")), e.getMessage());
}
}

/**
* Negative test cases for testing Boolean with TVP
*
Expand Down

0 comments on commit 729520d

Please sign in to comment.