Skip to content

Commit 1dc9eb7

Browse files
authored
Properly Insert Accented Characters When Using Bulk Copy and Non-Unicode Strings (#2727)
* Fix to handle accented characters in database when using useBulkCopyForBatchInsert=true and sendStringParametersAsUnicode=false * Addressed comments * Updated test case to verify results with all combinations for useBulkCopyForBatchInsert and sendStringParametersAsUnicode property
1 parent 726bd14 commit 1dc9eb7

File tree

3 files changed

+65
-63
lines changed

3 files changed

+65
-63
lines changed

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.math.BigDecimal;
99
import java.math.RoundingMode;
10+
import java.nio.charset.Charset;
1011
import java.sql.Types;
1112
import java.text.DecimalFormat;
1213
import java.text.MessageFormat;
@@ -34,6 +35,7 @@ class SQLServerBulkBatchInsertRecord extends SQLServerBulkRecord {
3435
private int batchParamIndex = -1;
3536
private List<String> columnList;
3637
private List<String> valueList;
38+
private Charset charset;
3739

3840
/*
3941
* Class name for logging.
@@ -44,10 +46,10 @@ class SQLServerBulkBatchInsertRecord extends SQLServerBulkRecord {
4446
* Constructs a SQLServerBulkBatchInsertRecord with the batch parameter, column list, value list, and encoding
4547
*/
4648
SQLServerBulkBatchInsertRecord(ArrayList<Parameter[]> batchParam, ArrayList<String> columnList,
47-
ArrayList<String> valueList, String encoding, boolean columnNameCaseSensitive) throws SQLServerException {
49+
ArrayList<String> valueList, Charset charset, boolean columnNameCaseSensitive) throws SQLServerException {
4850
initLoggerResources();
4951
if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) {
50-
loggerExternal.entering(loggerPackageName, loggerClassName, new Object[] {batchParam, encoding});
52+
loggerExternal.entering(loggerPackageName, loggerClassName, new Object[] {batchParam, charset.name()});
5153
}
5254

5355
if (null == batchParam) {
@@ -62,6 +64,7 @@ class SQLServerBulkBatchInsertRecord extends SQLServerBulkRecord {
6264
this.columnList = columnList;
6365
this.valueList = valueList;
6466
this.columnNameCaseSensitive = columnNameCaseSensitive;
67+
this.charset = charset;
6568
columnMetadata = new HashMap<>();
6669

6770
loggerExternal.exiting(loggerPackageName, loggerClassName);
@@ -193,7 +196,7 @@ else if (dateTimeFormatter != null)
193196
* If the data is already a string, return it as is.
194197
*/
195198
if (data instanceof byte[]) {
196-
return new String((byte[]) data);
199+
return new String((byte[]) data, charset);
197200
}
198201
return data;
199202
}

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2218,7 +2218,8 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL
22182218
}
22192219

22202220
SQLServerBulkBatchInsertRecord batchRecord = new SQLServerBulkBatchInsertRecord(
2221-
batchParamValues, bcOperationColumnList, bcOperationValueList, null, isDBColationCaseSensitive());
2221+
batchParamValues, bcOperationColumnList, bcOperationValueList,
2222+
connection.getDatabaseCollation().getCharset(), isDBColationCaseSensitive());
22222223

22232224
for (int i = 1; i <= rs.getColumnCount(); i++) {
22242225
Column c = rs.getColumn(i);
@@ -2426,7 +2427,8 @@ public long[] executeLargeBatch() throws SQLServerException, BatchUpdateExceptio
24262427
}
24272428

24282429
SQLServerBulkBatchInsertRecord batchRecord = new SQLServerBulkBatchInsertRecord(
2429-
batchParamValues, bcOperationColumnList, bcOperationValueList, null, isDBColationCaseSensitive());
2430+
batchParamValues, bcOperationColumnList, bcOperationValueList,
2431+
connection.getDatabaseCollation().getCharset(), isDBColationCaseSensitive());
24302432

24312433
for (int i = 1; i <= rs.getColumnCount(); i++) {
24322434
Column c = rs.getColumn(i);

src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java

Lines changed: 55 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1408,82 +1408,76 @@ private void getCreateTableTemporalSQL(String tableName) throws SQLException {
14081408
}
14091409

14101410
/**
1411-
* Test batch insert using bulk copy with string values when setSendStringParametersAsUnicode is true.
1411+
* Test string values using prepared statement using accented and unicode characters.
1412+
* This test covers all combinations of useBulkCopyForBatchInsert and sendStringParametersAsUnicode.
1413+
*
1414+
* @throws Exception
14121415
*/
14131416
@Test
1414-
public void testBulkInsertStringWhenSentAsUnicode() throws Exception {
1415-
String insertSQL = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableNameBulkString)
1416-
+ " (charCol, varcharCol, longvarcharCol, ncharCol, nvarcharCol, longnvarcharCol) VALUES (?, ?, ?, ?, ?, ?)";
1417-
1418-
String selectSQL = "SELECT charCol, varcharCol, longvarcharCol, ncharCol, nvarcharCol, longnvarcharCol FROM "
1419-
+ AbstractSQLGenerator.escapeIdentifier(tableNameBulkString);
1420-
1421-
try (Connection connection = PrepUtil.getConnection(
1422-
connectionString + ";useBulkCopyForBatchInsert=true;sendStringParametersAsUnicode=true;");
1423-
SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(insertSQL);
1424-
Statement stmt = (SQLServerStatement) connection.createStatement()) {
1425-
1426-
getCreateTableWithStringData();
1427-
1428-
pstmt.setString(1, "CHAR_VAL");
1429-
pstmt.setString(2, "VARCHAR_VALUE");
1430-
pstmt.setString(3, "LONGVARCHAR_VALUE_WITH_MORE_TEXT");
1431-
pstmt.setString(4, "NCHAR_VAL");
1432-
pstmt.setString(5, "NVARCHAR_VALUE");
1433-
pstmt.setString(6, "LONGNVARCHAR_VALUE_WITH_UNICODE_TEXT");
1434-
pstmt.addBatch();
1435-
pstmt.executeBatch();
1436-
1437-
// Validate inserted data
1438-
try (ResultSet rs = stmt.executeQuery(selectSQL)) {
1439-
assertTrue(rs.next(), "Expected at least one row in result set");
1440-
assertEquals("CHAR_VAL", rs.getString("charCol"));
1441-
assertEquals("VARCHAR_VALUE", rs.getString("varcharCol"));
1442-
assertEquals("LONGVARCHAR_VALUE_WITH_MORE_TEXT", rs.getString("longvarcharCol"));
1443-
assertEquals("NCHAR_VAL", rs.getString("ncharCol"));
1444-
assertEquals("NVARCHAR_VALUE", rs.getString("nvarcharCol"));
1445-
assertEquals("LONGNVARCHAR_VALUE_WITH_UNICODE_TEXT", rs.getString("longnvarcharCol"));
1446-
assertFalse(rs.next());
1417+
public void testBulkInsertStringAllCombinations() throws Exception {
1418+
boolean[] bulkCopyOptions = { true, false };
1419+
boolean[] unicodeOptions = { true, false };
1420+
for (boolean useBulkCopy : bulkCopyOptions) {
1421+
for (boolean sendUnicode : unicodeOptions) {
1422+
runBulkInsertStringTest(useBulkCopy, sendUnicode);
14471423
}
14481424
}
14491425
}
14501426

14511427
/**
1452-
* Test batch insert using bulk copy with string values when setSendStringParametersAsUnicode is false.
1428+
* Test batch insert using accented and unicode characters.
14531429
*/
1454-
@Test
1455-
public void testBulkInsertStringWhenNotSentAsUnicode() throws Exception {
1430+
public void runBulkInsertStringTest(boolean useBulkCopy, boolean sendUnicode) throws Exception {
14561431
String insertSQL = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableNameBulkString)
1457-
+ " (charCol, varcharCol, longvarcharCol, ncharCol, nvarcharCol, longnvarcharCol) VALUES (?, ?, ?, ?, ?, ?)";
1432+
+ " (charCol, varcharCol, longvarcharCol, ncharCol1, nvarcharCol1, longnvarcharCol1, "
1433+
+ "ncharCol2, nvarcharCol2, longnvarcharCol2) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
14581434

1459-
String selectSQL = "SELECT charCol, varcharCol, longvarcharCol, ncharCol, nvarcharCol, longnvarcharCol FROM "
1435+
String selectSQL = "SELECT charCol, varcharCol, longvarcharCol, ncharCol1, nvarcharCol1, "
1436+
+ "longnvarcharCol1, ncharCol2, nvarcharCol2, longnvarcharCol2 FROM "
14601437
+ AbstractSQLGenerator.escapeIdentifier(tableNameBulkString);
14611438

14621439
try (Connection connection = PrepUtil.getConnection(
1463-
connectionString + ";useBulkCopyForBatchInsert=true;sendStringParametersAsUnicode=false;");
1440+
connectionString + ";useBulkCopyForBatchInsert=" + useBulkCopy + ";sendStringParametersAsUnicode="
1441+
+ sendUnicode + ";");
14641442
SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(insertSQL);
14651443
Statement stmt = (SQLServerStatement) connection.createStatement()) {
14661444

14671445
getCreateTableWithStringData();
14681446

1469-
pstmt.setString(1, "CHAR_VAL");
1470-
pstmt.setString(2, "VARCHAR_VALUE");
1471-
pstmt.setString(3, "LONGVARCHAR_VALUE_WITH_MORE_TEXT");
1472-
pstmt.setString(4, "NCHAR_VAL");
1473-
pstmt.setString(5, "NVARCHAR_VALUE");
1474-
pstmt.setString(6, "LONGNVARCHAR_VALUE_WITH_UNICODE_TEXT");
1447+
String charValue = "Anaïs_Ni";
1448+
String varcharValue = "café";
1449+
String longVarcharValue = "Sørén Kierkégaard";
1450+
String ncharValue1 = "José Müll";
1451+
String nvarcharValue1 = "José Müller";
1452+
String longNvarcharValue1 = "François Saldaña";
1453+
String ncharValue2 = "Test1汉字😀";
1454+
String nvarcharValue2 = "汉字";
1455+
String longNvarcharValue2 = "日本語";
1456+
1457+
pstmt.setString(1, charValue);
1458+
pstmt.setString(2, varcharValue);
1459+
pstmt.setString(3, longVarcharValue);
1460+
pstmt.setString(4, ncharValue1);
1461+
pstmt.setString(5, nvarcharValue1);
1462+
pstmt.setString(6, longNvarcharValue1);
1463+
pstmt.setNString(7, ncharValue2);
1464+
pstmt.setNString(8, nvarcharValue2);
1465+
pstmt.setNString(9, longNvarcharValue2);
14751466
pstmt.addBatch();
14761467
pstmt.executeBatch();
14771468

14781469
// Validate inserted data
14791470
try (ResultSet rs = stmt.executeQuery(selectSQL)) {
14801471
assertTrue(rs.next(), "Expected at least one row in result set");
1481-
assertEquals("CHAR_VAL", rs.getString("charCol"));
1482-
assertEquals("VARCHAR_VALUE", rs.getString("varcharCol"));
1483-
assertEquals("LONGVARCHAR_VALUE_WITH_MORE_TEXT", rs.getString("longvarcharCol"));
1484-
assertEquals("NCHAR_VAL", rs.getString("ncharCol"));
1485-
assertEquals("NVARCHAR_VALUE", rs.getString("nvarcharCol"));
1486-
assertEquals("LONGNVARCHAR_VALUE_WITH_UNICODE_TEXT", rs.getString("longnvarcharCol"));
1472+
assertEquals(charValue, rs.getString("charCol"));
1473+
assertEquals(varcharValue, rs.getString("varcharCol"));
1474+
assertEquals(longVarcharValue, rs.getString("longvarcharCol"));
1475+
assertEquals(ncharValue1, rs.getString("ncharCol1"));
1476+
assertEquals(nvarcharValue1, rs.getString("nvarcharCol1"));
1477+
assertEquals(longNvarcharValue1, rs.getString("longnvarcharCol1"));
1478+
assertEquals(ncharValue2, rs.getString("ncharCol2"));
1479+
assertEquals(nvarcharValue2, rs.getString("nvarcharCol2"));
1480+
assertEquals(longNvarcharValue2, rs.getString("longnvarcharCol2"));
14871481
assertFalse(rs.next());
14881482
}
14891483
}
@@ -1493,12 +1487,15 @@ private void getCreateTableWithStringData() throws SQLException {
14931487
try (Statement stmt = connection.createStatement()) {
14941488
TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableNameBulkString), stmt);
14951489
String createTableSQL = "CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableNameBulkString) + " (" +
1496-
"charCol CHAR(8) NOT NULL, " +
1497-
"varcharCol VARCHAR(50) NOT NULL, " +
1498-
"longvarcharCol VARCHAR(MAX) NOT NULL, " +
1499-
"ncharCol NCHAR(9) NOT NULL, " +
1500-
"nvarcharCol NVARCHAR(50) NOT NULL, " +
1501-
"longnvarcharCol NVARCHAR(MAX) NOT NULL" + ")";
1490+
"charCol CHAR(8), " +
1491+
"varcharCol VARCHAR(50), " +
1492+
"longvarcharCol VARCHAR(MAX), " +
1493+
"ncharCol1 NCHAR(9), " +
1494+
"nvarcharCol1 NVARCHAR(50), " +
1495+
"longnvarcharCol1 NVARCHAR(MAX), " +
1496+
"ncharCol2 NCHAR(9), " +
1497+
"nvarcharCol2 NVARCHAR(50), " +
1498+
"longnvarcharCol2 NVARCHAR(MAX)" + ")";
15021499

15031500
stmt.execute(createTableSQL);
15041501
}

0 commit comments

Comments
 (0)