Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2092,7 +2092,7 @@ private void validateColumnMappings() throws SQLServerException {
throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND,
DriverError.NOT_SET, null);
}
} else if (0 > cm.destinationColumnOrdinal || destColumnCount < cm.destinationColumnOrdinal) {
} else if (0 > cm.destinationColumnOrdinal || !destColumnMetadata.containsKey(cm.destinationColumnOrdinal)) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumn"));
Object[] msgArgs = {cm.destinationColumnOrdinal};
throw new SQLServerException(form.format(msgArgs), SQLState.COL_NOT_FOUND, DriverError.NOT_SET,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,70 @@ public void testInsertWithBulkCopyPerformance() throws SQLException {
System.out.println("Insert for " + recordCount + " records in " + durationMs + " ms.");
}

/**
* GitHub issue 2847: Persisted computed columns break useBulkCopyForBatchInsert.
*
* Verifies that batch inserts using bulk copy work correctly when the target table
* contains a persisted computed column. The computed column is not included in the
* INSERT statement, and the bulk copy operation correctly ignores it while
* inserting the remaining data.
*
* @throws Exception
*/
@Test
public void testBulkCopyBatchInsertWithPersistedComputedColumn() throws Exception {

String tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("BulkCopyPersistedComputed"));
String insertSQL = "INSERT INTO " + tableName + " (QtyAvailable, UnitPrice) VALUES (?, ?)";

try (Connection connection = PrepUtil.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;");
SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(insertSQL);
Statement stmt = connection.createStatement()) {

TestUtils.dropTableIfExists(tableName, stmt);

// Create table with persisted computed column
String createTable = "CREATE TABLE " + tableName + " ("
+ "ProductID int IDENTITY(1,1) NOT NULL, "
+ "QtyAvailable SMALLINT, "
+ "InventoryValue AS QtyAvailable * UnitPrice PERSISTED, "
+ "UnitPrice MONEY)";
stmt.execute(createTable);

// Test batch insert
pstmt.setInt(1, 10);
pstmt.setBigDecimal(2, new BigDecimal("15.99"));
pstmt.addBatch();

pstmt.setInt(1, 20);
pstmt.setBigDecimal(2, new BigDecimal("25.50"));
pstmt.addBatch();

int[] updateCounts = pstmt.executeBatch();
assertEquals(2, updateCounts.length);

// Validate inserted rows, including computed values
try (ResultSet rs = stmt.executeQuery("SELECT ProductID, QtyAvailable, InventoryValue, UnitPrice FROM "
+ tableName + " ORDER BY ProductID")) {
assertTrue(rs.next());
assertEquals(1, rs.getInt(1));
assertEquals(10, rs.getInt(2));
assertEquals(new BigDecimal("159.9000"), rs.getBigDecimal(3));
assertEquals(new BigDecimal("15.9900"), rs.getBigDecimal(4));

assertTrue(rs.next());
assertEquals(2, rs.getInt(1));
assertEquals(20, rs.getInt(2));
assertEquals(new BigDecimal("510.0000"), rs.getBigDecimal(3));
assertEquals(new BigDecimal("25.5000"), rs.getBigDecimal(4));
}
} finally {
try (Connection connection = getConnection(); Statement stmt = connection.createStatement()) {
TestUtils.dropTableIfExists(tableName, stmt);
}
}
}

private void getCreateTableTemporalSQL(String tableName) throws SQLException {
try (Statement stmt = connection.createStatement()) {
TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt);
Expand Down
Loading