From 9d184086e95d9aba6757524d8997d7ececb57c64 Mon Sep 17 00:00:00 2001 From: Balazs Toth Date: Wed, 7 Dec 2022 19:49:10 +0100 Subject: [PATCH 1/4] Support bulk insert with random column name list --- .../jdbc/SQLServerPreparedStatement.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index d27dab448..595e287e0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -26,6 +26,7 @@ import java.util.Calendar; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; import java.util.Vector; import java.util.logging.Level; @@ -2091,6 +2092,7 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL SQLServerResultSet rs = stmt .executeQueryInternal("sp_executesql N'SET FMTONLY ON SELECT * FROM " + Util.escapeSingleQuotes(tableName) + " '")) { + Map columnMappings = null; if (null != columnList && columnList.size() > 0) { if (columnList.size() != valueList.size()) { @@ -2099,6 +2101,7 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL Object[] msgArgs = {columnList.size(), valueList.size()}; throw new IllegalArgumentException(form.format(msgArgs)); } + columnMappings = new HashMap<>(columnList.size()); } else { if (rs.getColumnCount() != valueList.size()) { MessageFormat form = new MessageFormat( @@ -2122,8 +2125,15 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL } else { jdbctype = ti.getSSType().getJDBCType().getIntValue(); } - batchRecord.addColumnMetadata(i, c.getColumnName(), jdbctype, ti.getPrecision(), - ti.getScale()); + if (null != columnList && columnList.size() > 0) { + int positionInSource = columnList.indexOf(c.getColumnName()) + 1; + columnMappings.put(positionInSource, i); + batchRecord.addColumnMetadata(positionInSource, c.getColumnName(), jdbctype, + ti.getPrecision(), ti.getScale()); + } else { + batchRecord.addColumnMetadata(i, c.getColumnName(), jdbctype, ti.getPrecision(), + ti.getScale()); + } } SQLServerBulkCopy bcOperation = new SQLServerBulkCopy(connection); @@ -2131,6 +2141,11 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL option.setBulkCopyTimeout(queryTimeout); bcOperation.setBulkCopyOptions(option); bcOperation.setDestinationTableName(tableName); + if (columnMappings != null) { + for (Entry pair : columnMappings.entrySet()) { + bcOperation.addColumnMapping(pair.getKey(), pair.getValue()); + } + } bcOperation.setStmtColumnEncriptionSetting(this.getStmtColumnEncriptionSetting()); bcOperation.setDestinationTableMetadata(rs); bcOperation.writeToServer(batchRecord); From 828fc88eacb421a4f173ee320f1472b3b774c0be Mon Sep 17 00:00:00 2001 From: Balazs Toth Date: Wed, 7 Dec 2022 23:32:22 +0100 Subject: [PATCH 2/4] JUnit testing --- .../jdbc/SQLServerPreparedStatement.java | 10 +++-- .../BatchExecutionWithBulkCopyTest.java | 41 +++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 595e287e0..d5da81794 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -2126,10 +2126,12 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL jdbctype = ti.getSSType().getJDBCType().getIntValue(); } if (null != columnList && columnList.size() > 0) { - int positionInSource = columnList.indexOf(c.getColumnName()) + 1; - columnMappings.put(positionInSource, i); - batchRecord.addColumnMetadata(positionInSource, c.getColumnName(), jdbctype, - ti.getPrecision(), ti.getScale()); + int columnIndex = columnList.indexOf(c.getColumnName()); + if (columnIndex > -1) { + columnMappings.put(columnIndex + 1, i); + batchRecord.addColumnMetadata(columnIndex + 1, c.getColumnName(), jdbctype, + ti.getPrecision(), ti.getScale()); + } } else { batchRecord.addColumnMetadata(i, c.getColumnName(), jdbctype, ti.getPrecision(), ti.getScale()); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index 4178c15df..e6e4a5217 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -721,6 +721,47 @@ public void testNonSupportedColumns() throws Exception { } } + @Test + public void testReverseColumnOrder() throws Exception { + String valid = "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (c2, c1) values " + + "(" + "?, " + "? " + ")"; + + try (Connection connection = PrepUtil.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); + SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); + Statement stmt = (SQLServerStatement) connection.createStatement();) { + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); + f1.setAccessible(true); + f1.set(connection, true); + + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + String createTable = "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (c1 varchar(1) not null, c2 varchar(3) not null)"; + stmt.execute(createTable); + + pstmt.setString(1, "One"); + pstmt.setString(2, "1"); + pstmt.addBatch(); + + pstmt.executeBatch(); + + try (ResultSet rs = stmt + .executeQuery("select c1, c2 from " + AbstractSQLGenerator.escapeIdentifier(tableName))) { + + Object[] expected = new Object[2]; + + expected[0] = 1; + expected[1] = "One"; + rs.next(); + + for (int i = 0; i < expected.length; i++) { + if (null != rs.getObject(i + 1)) { + assertEquals(expected[i].toString(), rs.getObject(i + 1).toString()); + } + } + } + } + } + @BeforeAll public static void setupTests() throws Exception { setConnection(); From fed7e10933d9f2bbae674280db248ea327dca5ef Mon Sep 17 00:00:00 2001 From: Balazs Toth Date: Thu, 8 Dec 2022 00:27:32 +0100 Subject: [PATCH 3/4] Improve JUnit test --- .../preparedStatement/BatchExecutionWithBulkCopyTest.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index e6e4a5217..16b0fc73a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -735,7 +735,7 @@ public void testReverseColumnOrder() throws Exception { TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); String createTable = "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (c1 varchar(1) not null, c2 varchar(3) not null)"; + + " (c1 varchar(1), c2 varchar(3))"; stmt.execute(createTable); pstmt.setString(1, "One"); @@ -749,14 +749,12 @@ public void testReverseColumnOrder() throws Exception { Object[] expected = new Object[2]; - expected[0] = 1; + expected[0] = "1"; expected[1] = "One"; rs.next(); for (int i = 0; i < expected.length; i++) { - if (null != rs.getObject(i + 1)) { - assertEquals(expected[i].toString(), rs.getObject(i + 1).toString()); - } + assertEquals(expected[i], rs.getObject(i + 1)); } } } From 8b2dca1a92a90a1f9e446e87b7c884fc46f5ca5b Mon Sep 17 00:00:00 2001 From: Terry <32403408+tkyc@users.noreply.github.com> Date: Fri, 3 Mar 2023 12:34:22 -0800 Subject: [PATCH 4/4] Update src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java Co-authored-by: Jeffery Wasty --- .../jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index 16b0fc73a..dbccf4e7d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -728,7 +728,7 @@ public void testReverseColumnOrder() throws Exception { try (Connection connection = PrepUtil.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); - Statement stmt = (SQLServerStatement) connection.createStatement();) { + Statement stmt = connection.createStatement()) { Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); f1.setAccessible(true); f1.set(connection, true);