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

Performance | Improve performance of SQLServerDataTable.internalAddRow() function #990

Merged
merged 15 commits into from
May 22, 2019
Merged
228 changes: 117 additions & 111 deletions src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,37 +157,50 @@ public synchronized void addRow(Object... values) throws SQLServerException {
*/
private void internalAddrow(JDBCType jdbcType, Object val, List<Object> rowValues,
Map.Entry<Integer, SQLServerDataColumn> pair) throws SQLServerException {

SQLServerDataColumn currentColumnMetadata = pair.getValue();
int key = pair.getKey();
rowValues.add(key, val);

boolean isColumnMetadataUpdated = false;
boolean bValueNull;
int nValueLen;

switch (jdbcType) {
case BIGINT:
rowValues.set(key, (null == val) ? null : Long.parseLong(val.toString()));
break;

case BIT:
rowValues.set(key, (null == val) ? null : Boolean.parseBoolean(val.toString()));;
break;
if (null != val) {
SQLServerDataColumn currentColumnMetadata = pair.getValue();
int nValueLen;

switch (jdbcType) {
case BIGINT:
rowValues.set(key, (val instanceof Long) ? val : Long.parseLong(val.toString()));
break;

case BIT:
if (val instanceof Boolean) {
rowValues.set(key, val);
} else {
String valString = val.toString();

if (valString.equals("0") || valString.equalsIgnoreCase(Boolean.FALSE.toString())) {
rowValues.set(key, Boolean.FALSE);
} else if (valString.equals("1") || valString.equalsIgnoreCase(Boolean.TRUE.toString())) {
rowValues.set(key, Boolean.TRUE);
} else {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_TVPInvalidColumnValue"));
Object[] msgArgs = {jdbcType};
throw new SQLServerException(null, form.format(msgArgs), null, 0, false);
}
}
break;

case INTEGER:
rowValues.set(key, (null == val) ? null : Integer.parseInt(val.toString()));
break;
case INTEGER:
rowValues.set(key, (val instanceof Integer) ? val : Integer.parseInt(val.toString()));
break;

case SMALLINT:
case TINYINT:
rowValues.set(key, (null == val) ? null : Short.parseShort(val.toString()));
break;
case SMALLINT:
case TINYINT:
rowValues.set(key, (val instanceof Short) ? val : Short.parseShort(val.toString()));
break;

case DECIMAL:
case NUMERIC:
BigDecimal bd = null;
if (null != val) {
case DECIMAL:
case NUMERIC:
BigDecimal bd = null;
boolean isColumnMetadataUpdated = false;
bd = new BigDecimal(val.toString());
// BigDecimal#precision returns number of digits in the unscaled value.
// Say, for value 0.01, it returns 1 but the precision should be 3 for SQLServer
Expand All @@ -213,92 +226,85 @@ private void internalAddrow(JDBCType jdbcType, Object val, List<Object> rowValue
+ currentColumnMetadata.numberOfDigitsIntegerPart;
columnMetadata.put(pair.getKey(), currentColumnMetadata);
}
}
rowValues.set(key, bd);
break;

case DOUBLE:
rowValues.set(key, (null == val) ? null : Double.parseDouble(val.toString()));
break;

case FLOAT:
case REAL:
rowValues.set(key, (null == val) ? null : Float.parseFloat(val.toString()));
break;

case TIMESTAMP_WITH_TIMEZONE:
case TIME_WITH_TIMEZONE:
case DATE:
case TIME:
case TIMESTAMP:
case DATETIMEOFFSET:
case DATETIME:
case SMALLDATETIME:
// Sending temporal types as string. Error from database is thrown if parsing fails
// no need to send precision for temporal types, string literal will never exceed
// DataTypes.SHORT_VARTYPE_MAX_BYTES

if (null == val)
rowValues.set(key, null);
// java.sql.Date, java.sql.Time and java.sql.Timestamp are subclass of java.util.Date
else if (val instanceof java.util.Date)
rowValues.set(key, val.toString());
else if (val instanceof microsoft.sql.DateTimeOffset)
rowValues.set(key, val.toString());
else if (val instanceof OffsetDateTime)
rowValues.set(key, val.toString());
else if (val instanceof OffsetTime)
rowValues.set(key, val.toString());
else
rowValues.set(key, (String) val);
break;

case BINARY:
case VARBINARY:
case LONGVARBINARY:
bValueNull = (null == val);
nValueLen = bValueNull ? 0 : ((byte[]) val).length;

if (nValueLen > currentColumnMetadata.precision) {
currentColumnMetadata.precision = nValueLen;
columnMetadata.put(pair.getKey(), currentColumnMetadata);
}
rowValues.set(key, (bValueNull) ? null : (byte[]) val);

break;

case CHAR:
if (val instanceof UUID && (val != null))
val = val.toString();
case VARCHAR:
case NCHAR:
case NVARCHAR:
case LONGVARCHAR:
case LONGNVARCHAR:
case SQLXML:
bValueNull = (null == val);
nValueLen = bValueNull ? 0 : (2 * ((String) val).length());

if (nValueLen > currentColumnMetadata.precision) {
currentColumnMetadata.precision = nValueLen;
columnMetadata.put(pair.getKey(), currentColumnMetadata);
}
rowValues.set(key, (bValueNull) ? null : (String) val);
break;
case SQL_VARIANT:
JDBCType internalJDBCType;
if (null == val) { // TODO:Check this later
throw new SQLServerException(SQLServerException.getErrString("R_invalidValueForTVPWithSQLVariant"),
null);
}
JavaType javaType = JavaType.of(val);
internalJDBCType = javaType.getJDBCType(SSType.UNKNOWN, jdbcType);
internalAddrow(internalJDBCType, val, rowValues, pair);
break;
default:
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedDataTypeTVP"));
Object[] msgArgs = {jdbcType};
throw new SQLServerException(null, form.format(msgArgs), null, 0, false);
rowValues.set(key, bd);
break;

case DOUBLE:
rowValues.set(key, (val instanceof Double) ? val : Double.parseDouble(val.toString()));
break;

case FLOAT:
case REAL:
rowValues.set(key, (val instanceof Float) ? val : Float.parseFloat(val.toString()));
break;

case TIMESTAMP_WITH_TIMEZONE:
case TIME_WITH_TIMEZONE:
case DATE:
case TIME:
case TIMESTAMP:
case DATETIMEOFFSET:
case DATETIME:
case SMALLDATETIME:
// Sending temporal types as string. Error from database is thrown if parsing fails
// no need to send precision for temporal types, string literal will never exceed
// DataTypes.SHORT_VARTYPE_MAX_BYTES

// java.sql.Date, java.sql.Time and java.sql.Timestamp are subclass of java.util.Date
if (val instanceof java.util.Date || val instanceof microsoft.sql.DateTimeOffset
|| val instanceof OffsetDateTime || val instanceof OffsetTime)
rowValues.set(key, val.toString());
else
rowValues.set(key, val);
break;

case BINARY:
case VARBINARY:
case LONGVARBINARY:
nValueLen = ((byte[]) val).length;

if (nValueLen > currentColumnMetadata.precision) {
currentColumnMetadata.precision = nValueLen;
columnMetadata.put(pair.getKey(), currentColumnMetadata);
}
rowValues.set(key, val);
break;

case CHAR:
case VARCHAR:
case NCHAR:
case NVARCHAR:
case LONGVARCHAR:
case LONGNVARCHAR:
case SQLXML:
if (val instanceof UUID)
val = val.toString();
nValueLen = (2 * ((String) val).length());
cheenamalhotra marked this conversation as resolved.
Show resolved Hide resolved

if (nValueLen > currentColumnMetadata.precision) {
currentColumnMetadata.precision = nValueLen;
columnMetadata.put(pair.getKey(), currentColumnMetadata);
}
rowValues.set(key, val);
break;

case SQL_VARIANT:
JDBCType internalJDBCType;
JavaType javaType = JavaType.of(val);
internalJDBCType = javaType.getJDBCType(SSType.UNKNOWN, jdbcType);
internalAddrow(internalJDBCType, val, rowValues, pair);
break;

default:
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedDataTypeTVP"));
Object[] msgArgs = {jdbcType};
throw new SQLServerException(null, form.format(msgArgs), null, 0, false);
}
} else {
if (jdbcType == JDBCType.SQL_VARIANT) {
throw new SQLServerException(SQLServerException.getErrString("R_invalidValueForTVPWithSQLVariant"),
null);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ public void testNull() throws SQLException {
try {
tvp.addRow((Date) null);
} catch (Exception e) {
assertTrue(e.getMessage().startsWith("Use of TVPs containing null sql_variant columns is not supported."));
assertTrue(e.getMessage().startsWith(TestUtils.rBundle.getString("R_invalidValueForTVPWithSQLVariant")));
}

try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) conn.prepareStatement(
Expand Down
Loading