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

Apply timezone to Timestamp when using bulkcopy for batch insert #2291

Merged
merged 10 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -1056,7 +1056,7 @@ private void writeInternal(byte[] b, int off, int len) throws IOException {
private final class ProxyInputStream extends InputStream {
private InputStream filteredStream;

private final transient Lock proxyInputStreamLock = new ReentrantLock();
private final Lock proxyInputStreamLock = new ReentrantLock();

/**
* Bytes that have been read by a poll(s).
Expand Down Expand Up @@ -3825,6 +3825,20 @@ void writeDate(String value) throws SQLServerException {
SSType.DATE);
}

void writeDate(long utcMillis, Calendar cal) throws SQLServerException {
GregorianCalendar calendar = initializeCalender(TimeZone.getDefault());

// Load the calendar with the desired value
calendar.setTimeInMillis(utcMillis);
if (cal != null) {
calendar.setTimeZone(cal.getTimeZone());
}

writeScaledTemporal(calendar, 0, // subsecond nanos (none for a date value)
0, // scale (dates are not scaled)
SSType.DATE);
}

void writeTime(java.sql.Timestamp value, int scale) throws SQLServerException {
GregorianCalendar calendar = initializeCalender(TimeZone.getDefault());
long utcMillis; // Value to which the calendar is to be set (in milliseconds 1/1/1970 00:00:00 GMT)
Expand All @@ -3838,6 +3852,20 @@ void writeTime(java.sql.Timestamp value, int scale) throws SQLServerException {
writeScaledTemporal(calendar, subSecondNanos, scale, SSType.TIME);
}

void writeTime(java.sql.Timestamp value, int scale, Calendar cal) throws SQLServerException {
GregorianCalendar calendar = initializeCalender(TimeZone.getDefault());
long utcMillis = value.getTime(); // Value to which the calendar is to be set (in milliseconds 1/1/1970 00:00:00 GMT)
int subSecondNanos = value.getNanos();

// Load the calendar with the desired value
calendar.setTimeInMillis(utcMillis);
if (cal != null) {
calendar.setTimeZone(cal.getTimeZone());
}

writeScaledTemporal(calendar, subSecondNanos, scale, SSType.TIME);
}

void writeDateTimeOffset(Object value, int scale, SSType destSSType) throws SQLServerException {
GregorianCalendar calendar;
TimeZone timeZone; // Time zone to associate with the value in the Gregorian calendar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,14 +498,14 @@ CallableStatement prepareCall(String sql, int nType, int nConcur, int nHold,
*/
void setCalcBigDecimalPrecision(boolean calcBigDecimalPrecision);

/**
/**
* Specifies the flag for using Bulk Copy API for batch insert operations.
*
* @param useBulkCopyForBatchInsert
* boolean value for useBulkCopyForBatchInsert.
*/
void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert) ;
void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert);

/**
* Returns the useBulkCopyForBatchInsert value.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class SQLServerBulkBatchInsertRecord extends SQLServerBulkRecord {
*/
private static final long serialVersionUID = -955998113956445541L;

private transient List<Parameter[]> batchParam;
transient List<Parameter[]> batchParam;
private int batchParamIndex = -1;
private List<String> columnList;
private List<String> valueList;
Expand Down
35 changes: 27 additions & 8 deletions src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java
Original file line number Diff line number Diff line change
Expand Up @@ -2064,7 +2064,7 @@ private void writeNullToTdsWriter(TDSWriter tdsWriter, int srcJdbcType,

private void writeColumnToTdsWriter(TDSWriter tdsWriter, int bulkPrecision, int bulkScale, int bulkJdbcType,
boolean bulkNullable, // should it be destNullable instead?
int srcColOrdinal, int destColOrdinal, boolean isStreaming, Object colValue) throws SQLServerException {
int srcColOrdinal, int destColOrdinal, boolean isStreaming, Object colValue, Calendar cal) throws SQLServerException {
SSType destSSType = destColumnMetadata.get(destColOrdinal).ssType;

bulkPrecision = validateSourcePrecision(bulkPrecision, bulkJdbcType,
Expand Down Expand Up @@ -2480,10 +2480,17 @@ else if (4 >= bulkScale)
tdsWriter.writeByte((byte) 0x07);
else
tdsWriter.writeByte((byte) 0x08);
String timeStampValue = colValue.toString();
tdsWriter.writeTime(java.sql.Timestamp.valueOf(timeStampValue), bulkScale);

Timestamp ts;
if (colValue instanceof java.sql.Timestamp) {
ts = (Timestamp) colValue;
} else {
ts = Timestamp.valueOf(colValue.toString());
}

tdsWriter.writeTime(ts, bulkScale, cal);
// Send only the date part
tdsWriter.writeDate(timeStampValue.substring(0, timeStampValue.lastIndexOf(' ')));
tdsWriter.writeDate(ts.getTime(), cal);
}
}
break;
Expand Down Expand Up @@ -2981,7 +2988,7 @@ private Object readColumnFromResultSet(int srcColOrdinal, int srcJdbcType, boole
* Reads the given column from the result set current row and writes the data to tdsWriter.
*/
private void writeColumn(TDSWriter tdsWriter, int srcColOrdinal, int destColOrdinal,
Object colValue) throws SQLServerException {
Object colValue, Calendar cal) throws SQLServerException {
String destName = destColumnMetadata.get(destColOrdinal).columnName;
int srcPrecision, srcScale, destPrecision, srcJdbcType;
SSType destSSType = null;
Expand Down Expand Up @@ -3102,7 +3109,7 @@ else if (null != serverBulkData && (null == destCryptoMeta)) {
}
}
writeColumnToTdsWriter(tdsWriter, srcPrecision, srcScale, srcJdbcType, srcNullable, srcColOrdinal,
destColOrdinal, isStreaming, colValue);
destColOrdinal, isStreaming, colValue, cal);
}

/**
Expand Down Expand Up @@ -3633,7 +3640,7 @@ private boolean writeBatchData(TDSWriter tdsWriter, TDSCommand command,
// Loop for each destination column. The mappings is a many to one mapping
// where multiple source columns can be mapped to one destination column.
for (ColumnMapping columnMapping : columnMappings) {
writeColumn(tdsWriter, columnMapping.sourceColumnOrdinal, columnMapping.destinationColumnOrdinal,
writeColumn(tdsWriter, columnMapping.sourceColumnOrdinal, columnMapping.destinationColumnOrdinal, null,
null // cell
// value is
// retrieved
Expand All @@ -3647,20 +3654,32 @@ private boolean writeBatchData(TDSWriter tdsWriter, TDSCommand command,
else {
// Get all the column values of the current row.
Object[] rowObjects;
Parameter[] params = null;

try {
rowObjects = serverBulkData.getRowData();
if (serverBulkData instanceof SQLServerBulkBatchInsertRecord) {
params = ((SQLServerBulkBatchInsertRecord) serverBulkData).batchParam.get(row);
}
} catch (Exception ex) {
// if no more data available to retrive
throw new SQLServerException(SQLServerException.getErrString("R_unableRetrieveSourceData"), ex);
}

for (ColumnMapping columnMapping : columnMappings) {

Object rowObject = rowObjects[columnMapping.sourceColumnOrdinal - 1];
Calendar cal = null;

if (rowObject instanceof Timestamp && params != null) {
cal = params[columnMapping.sourceColumnOrdinal - 1].getInputDTV().getCalendar();
}

// If the SQLServerBulkCSVRecord does not have metadata for columns, it returns strings in the
// object array.
// COnvert the strings using destination table types.
writeColumn(tdsWriter, columnMapping.sourceColumnOrdinal, columnMapping.destinationColumnOrdinal,
rowObjects[columnMapping.sourceColumnOrdinal - 1]);
rowObject, cal);
}
}
row++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2589,7 +2589,8 @@ public void registerOutParameter(String parameterName, int sqlType) throws SQLSe
loggerExternal.entering(getClassNameLogging(), "registerOutParameter",
new Object[] {parameterName, sqlType});
checkClosed();
registerOutParameterByName(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), sqlType);
registerOutParameterByName(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD),
sqlType);
loggerExternal.exiting(getClassNameLogging(), "registerOutParameter");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -712,14 +712,15 @@ public boolean getCalcBigDecimalPrecision() {
public void setCalcBigDecimalPrecision(boolean calcBigDecimalPrecision) {
wrappedConnection.setCalcBigDecimalPrecision(calcBigDecimalPrecision);
}

/**
* Returns the useBulkCopyForBatchInsert value.
*
* @return flag for using Bulk Copy API for batch insert operations.
*/
@Override
public boolean getUseBulkCopyForBatchInsert() {
return wrappedConnection.getUseBulkCopyForBatchInsert();
return wrappedConnection.getUseBulkCopyForBatchInsert();
}

/**
Expand All @@ -730,6 +731,6 @@ public boolean getUseBulkCopyForBatchInsert() {
*/
@Override
public void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert) {
wrappedConnection.setUseBulkCopyForBatchInsert(useBulkCopyForBatchInsert);
wrappedConnection.setUseBulkCopyForBatchInsert(useBulkCopyForBatchInsert);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -907,8 +907,8 @@ public final class SQLServerDriver implements java.sql.Driver {
Boolean.toString(SQLServerDriverBooleanProperty.USE_DEFAULT_JAAS_CONFIG.getDefaultValue()), false,
TRUE_FALSE),
new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.CALC_BIG_DECIMAL_PRECISION.toString(),
Boolean.toString(SQLServerDriverBooleanProperty.CALC_BIG_DECIMAL_PRECISION.getDefaultValue()), false,
TRUE_FALSE),
Boolean.toString(SQLServerDriverBooleanProperty.CALC_BIG_DECIMAL_PRECISION.getDefaultValue()),
false, TRUE_FALSE),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.SSL_PROTOCOL.toString(),
SQLServerDriverStringProperty.SSL_PROTOCOL.getDefaultValue(), false,
new String[] {SSLProtocol.TLS.toString(), SSLProtocol.TLS_V10.toString(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3422,6 +3422,7 @@ public final void setTimestamp(int n, java.sql.Timestamp x, java.util.Calendar c
if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {n, x, cal});
checkClosed();

setValue(n, JDBCType.TIMESTAMP, x, JavaType.TIMESTAMP, cal, false);
loggerExternal.exiting(getClassNameLogging(), "setTimestamp");
}
Expand All @@ -3433,6 +3434,7 @@ public final void setTimestamp(int n, java.sql.Timestamp x, java.util.Calendar c
if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {n, x, cal, forceEncrypt});
checkClosed();

setValue(n, JDBCType.TIMESTAMP, x, JavaType.TIMESTAMP, cal, forceEncrypt);
loggerExternal.exiting(getClassNameLogging(), "setTimestamp");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ public void testDataSource() throws SQLServerException {
assertEquals(booleanPropValue, ds.getUseFlexibleCallableStatements(),
TestResource.getResource("R_valuesAreDifferent"));
ds.setCalcBigDecimalPrecision(booleanPropValue);
assertEquals(booleanPropValue, ds.getCalcBigDecimalPrecision(), TestResource.getResource("R_valuesAreDifferent"));
assertEquals(booleanPropValue, ds.getCalcBigDecimalPrecision(),
TestResource.getResource("R_valuesAreDifferent"));

ds.setServerCertificate(stringPropValue);
assertEquals(stringPropValue, ds.getServerCertificate(), TestResource.getResource("R_valuesAreDifferent"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void testBasicConnectionAAD() throws Exception {
basicReconnect("jdbc:sqlserver://" + azureServer + ";database=" + azureDatabase + ";user="
+ azureUserName + ";password=" + azurePassword
+ ";loginTimeout=90;Authentication=ActiveDirectoryPassword");
retry = THROTTLE_RETRY_COUNT + 1;
retry = THROTTLE_RETRY_COUNT + 1;
} catch (Exception e) {
if (e.getMessage().matches(TestUtils.formatErrorMsg("R_crClientAllRecoveryAttemptsFailed"))) {
System.out.println(e.getMessage() + ". Recovery failed, retry #" + retry + " in "
Expand Down
Loading
Loading