package com.microsoft.sqlserver.jdbc;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Types;
import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
class SQLServerBulkBatchInsertRecord extends SQLServerBulkRecord {
private static final long serialVersionUID = -955998113956445541L;
private List<Parameter[]> batchParam;
private int batchParamIndex = -1;
private List<String> columnList;
private List<String> valueList;
private static final String loggerClassName = "SQLServerBulkBatchInsertRecord";
SQLServerBulkBatchInsertRecord(ArrayList<Parameter[]> batchParam, ArrayList<String> columnList,
ArrayList<String> valueList, String encoding) throws SQLServerException {
initLoggerResources();
if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) {
loggerExternal.entering(loggerPackageName, loggerClassName,
new Object[] {batchParam, encoding});
}
if (null == batchParam) {
throwInvalidArgument("batchParam");
}
if (null == valueList) {
throwInvalidArgument("valueList");
}
this.batchParam = batchParam;
this.columnList = columnList;
this.valueList = valueList;
columnMetadata = new HashMap<>();
loggerExternal.exiting(loggerPackageName, loggerClassName);
}
private void initLoggerResources() {
super.loggerPackageName = "com.microsoft.sqlserver.jdbc.SQLServerBulkBatchInsertRecord";
}
private Object convertValue(ColumnMetadata cm, Object data) throws SQLServerException {
switch (cm.columnType) {
case Types.INTEGER: {
DecimalFormat decimalFormatter = new DecimalFormat("#");
decimalFormatter.setRoundingMode(RoundingMode.DOWN);
String formatedfInput = decimalFormatter.format(Double.parseDouble(data.toString()));
return Integer.valueOf(formatedfInput);
}
case Types.TINYINT:
case Types.SMALLINT: {
DecimalFormat decimalFormatter = new DecimalFormat("#");
decimalFormatter.setRoundingMode(RoundingMode.DOWN);
String formatedfInput = decimalFormatter.format(Double.parseDouble(data.toString()));
return Short.valueOf(formatedfInput);
}
case Types.BIGINT: {
BigDecimal bd = new BigDecimal(data.toString().trim());
try {
return bd.setScale(0, RoundingMode.DOWN).longValueExact();
} catch (ArithmeticException ex) {
String value = "'" + data + "'";
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue"));
throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(cm.columnType)}), null, 0,
ex);
}
}
case Types.DECIMAL:
case Types.NUMERIC: {
BigDecimal bd = new BigDecimal(data.toString().trim());
return bd.setScale(cm.scale, RoundingMode.HALF_UP);
}
case Types.BIT: {
try {
return (0 == Double.parseDouble(data.toString())) ? Boolean.FALSE : Boolean.TRUE;
} catch (NumberFormatException e) {
return Boolean.parseBoolean(data.toString());
}
}
case Types.REAL: {
return Float.parseFloat(data.toString());
}
case Types.DOUBLE: {
return Double.parseDouble(data.toString());
}
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
case Types.BLOB: {
if (data instanceof byte[]) {
return data;
} else {
String binData = data.toString().trim();
if (binData.startsWith("0x") || binData.startsWith("0X")) {
return binData.substring(2);
} else {
return binData;
}
}
}
case java.sql.Types.TIME_WITH_TIMEZONE: {
OffsetTime offsetTimeValue;
if (null != cm.dateTimeFormatter)
offsetTimeValue = OffsetTime.parse(data.toString(), cm.dateTimeFormatter);
else if (timeFormatter != null)
offsetTimeValue = OffsetTime.parse(data.toString(), timeFormatter);
else
offsetTimeValue = OffsetTime.parse(data.toString());
return offsetTimeValue;
}
case java.sql.Types.TIMESTAMP_WITH_TIMEZONE: {
OffsetDateTime offsetDateTimeValue;
if (null != cm.dateTimeFormatter)
offsetDateTimeValue = OffsetDateTime.parse(data.toString(), cm.dateTimeFormatter);
else if (dateTimeFormatter != null)
offsetDateTimeValue = OffsetDateTime.parse(data.toString(), dateTimeFormatter);
else
offsetDateTimeValue = OffsetDateTime.parse(data.toString());
return offsetDateTimeValue;
}
case Types.NULL: {
return null;
}
case Types.DATE:
case Types.CHAR:
case Types.NCHAR:
case Types.VARCHAR:
case Types.NVARCHAR:
case Types.LONGVARCHAR:
case Types.LONGNVARCHAR:
case Types.CLOB:
default: {
return data;
}
}
}
private String removeSingleQuote(String s) {
int len = s.length();
return (s.charAt(0) == '\'' && s.charAt(len - 1) == '\'') ? s.substring(1, len - 1) : s;
}
@Override
public Object[] getRowData() throws SQLServerException {
Object[] data = new Object[columnMetadata.size()];
int valueIndex = 0;
String valueData;
Object rowData;
int columnListIndex = 0;
if (null != columnList && columnList.size() != valueList.size()) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_DataSchemaMismatch"));
Object[] msgArgs = {};
throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null);
}
for (Entry<Integer, ColumnMetadata> pair : columnMetadata.entrySet()) {
int index = pair.getKey() - 1;
if (null == columnList || columnList.size() == 0) {
valueData = valueList.get(index);
if ("?".equalsIgnoreCase(valueData)) {
rowData = batchParam.get(batchParamIndex)[valueIndex++].getSetterValue();
} else if ("null".equalsIgnoreCase(valueData)) {
rowData = null;
}
else {
rowData = removeSingleQuote(valueData);
}
}
else {
if (columnList.size() > columnListIndex
&& columnList.get(columnListIndex).equalsIgnoreCase(columnMetadata.get(index + 1).columnName)) {
valueData = valueList.get(columnListIndex);
if ("?".equalsIgnoreCase(valueData)) {
rowData = batchParam.get(batchParamIndex)[valueIndex++].getSetterValue();
} else if ("null".equalsIgnoreCase(valueData)) {
rowData = null;
} else {
rowData = removeSingleQuote(valueData);
}
columnListIndex++;
} else {
rowData = null;
}
}
try {
if (null == rowData) {
data[index] = null;
continue;
} else if (0 == rowData.toString().length()) {
data[index] = "";
continue;
}
data[index] = convertValue(pair.getValue(), rowData);
} catch (IllegalArgumentException e) {
String value = "'" + rowData + "'";
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue"));
throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(pair.getValue().columnType)}),
null, 0, e);
} catch (ArrayIndexOutOfBoundsException e) {
throw new SQLServerException(SQLServerException.getErrString("R_DataSchemaMismatch"), e);
}
}
return data;
}
@Override
void addColumnMetadataInternal(int positionInSource, String name, int jdbcType, int precision, int scale,
DateTimeFormatter dateTimeFormatter) throws SQLServerException {
if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) {
loggerExternal.entering(loggerPackageName, "addColumnMetadata",
new Object[] {positionInSource, name, jdbcType, precision, scale});
}
String colName = "";
if (0 >= positionInSource) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumnOrdinal"));
Object[] msgArgs = {positionInSource};
throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null);
}
if (null != name)
colName = name.trim();
else if ((null != columnNames) && (columnNames.length >= positionInSource))
colName = columnNames[positionInSource - 1];
if ((null != columnNames) && (positionInSource > columnNames.length)) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn"));
Object[] msgArgs = {positionInSource};
throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET, null);
}
checkDuplicateColumnName(positionInSource, name);
switch (jdbcType) {
case java.sql.Types.DATE:
case java.sql.Types.TIME:
case java.sql.Types.TIMESTAMP:
case microsoft.sql.Types.DATETIMEOFFSET:
columnMetadata.put(positionInSource,
new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter));
break;
case java.sql.Types.SQLXML:
columnMetadata.put(positionInSource,
new ColumnMetadata(colName, java.sql.Types.LONGNVARCHAR, precision, scale, dateTimeFormatter));
break;
case java.sql.Types.FLOAT:
columnMetadata.put(positionInSource,
new ColumnMetadata(colName, java.sql.Types.DOUBLE, precision, scale, dateTimeFormatter));
break;
case java.sql.Types.BOOLEAN:
columnMetadata.put(positionInSource,
new ColumnMetadata(colName, java.sql.Types.BIT, precision, scale, dateTimeFormatter));
break;
default:
columnMetadata.put(positionInSource,
new ColumnMetadata(colName, jdbcType, precision, scale, dateTimeFormatter));
}
loggerExternal.exiting(loggerPackageName, "addColumnMetadata");
}
@Override
public boolean next() throws SQLServerException {
batchParamIndex++;
return batchParamIndex < batchParam.size();
}
}