diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 78ed757fb..73a8a732b 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -788,20 +788,6 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { return false; } - /** - * Override TDS token processing behavior for PreparedStatement. - * For regular Statement, the execute API for INSERT requires reading an additional explicit - * TDS_DONE token that contains the actual update count returned by the server. - * PreparedStatement does not require this additional token processing, unless - * generated keys were requested (which requires processing additional TDS tokens). - */ - @Override - protected boolean hasUpdateCountTDSTokenForInsertCmd() { - // When generated keys are requested, we need to process additional TDS tokens - // to properly locate the ResultSet containing the generated keys - return bRequestedGeneratedKeys; - } - /** * Sends the statement parameters by RPC. */ diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 12997df49..88c2ecced 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -1602,7 +1602,7 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { return false; // For Insert operations, check if additional TDS_DONE token processing is required. - if (hasUpdateCountTDSTokenForInsertCmd() && (StreamDone.CMD_INSERT == doneToken.getCurCmd()) && (-1 != doneToken.getUpdateCount()) + if ((StreamDone.CMD_INSERT == doneToken.getCurCmd()) && (-1 != doneToken.getUpdateCount()) && EXECUTE == executeMethod) { return true; } @@ -1845,19 +1845,6 @@ boolean consumeExecOutParam(TDSReader tdsReader) throws SQLServerException { return false; } - /** - * Determines whether to continue processing additional TDS_DONE tokens for INSERT statements. - * For INSERT operations, regular Statement requires reading an additional TDS_DONE token that contains - * the actual update count. This method can be overridden by subclasses to customize - * TDS token processing behavior. - * - * @return true to continue processing more tokens to get the actual update count for INSERT operations - */ - protected boolean hasUpdateCountTDSTokenForInsertCmd() { - // For Insert, we must fetch additional TDS_DONE token that comes with the actual update count - return true; - } - // --------------------------JDBC 2.0----------------------------- @Override diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java index 78ded1edd..33badf1c6 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java @@ -3405,6 +3405,56 @@ public void testPreparedStatementWithTriggersAndGeneratedKeys() throws SQLExcept } } + /** + * Tests PreparedStatement INSERT with multiple values and trigger to validate update count. + * Reproduces the scenario where INSERT INTO TABLE (col) VALUES (?), (?) should return 2 but returns 1. + * This test validates the fix for TDS token processing with triggers that affect update counts. + * + * @throws SQLException + */ + @Test + public void testPreparedStatementInsertMultipleValuesWithTrigger() throws SQLException { + // Create separate test tables to avoid conflicts with existing setup + String testTableA = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("UpdateCountTestTableA")); + String testTableB = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("UpdateCountTestTableB")); + String testTrigger = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("UpdateCountTestTrigger")); + + try (Connection conn = getConnection(); + Statement stmt = conn.createStatement()) { + + TestUtils.dropTriggerIfExists(testTrigger, stmt); + TestUtils.dropTableIfExists(testTableB, stmt); + TestUtils.dropTableIfExists(testTableA, stmt); + + stmt.executeUpdate("CREATE TABLE " + testTableA + " (ID int NOT NULL IDENTITY(1,1) PRIMARY KEY, NAME varchar(32))"); + stmt.executeUpdate("CREATE TABLE " + testTableB + " (ID int NOT NULL IDENTITY(1,1) PRIMARY KEY)"); + + + stmt.executeUpdate("CREATE TRIGGER " + testTrigger + " ON " + testTableA + " FOR INSERT AS " + + "INSERT INTO " + testTableB + " DEFAULT VALUES"); + + // Test case: INSERT with multiple values should return correct update count + String sql = "INSERT INTO " + testTableA + " (NAME) VALUES (?), (?)"; + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, "value1"); + ps.setString(2, "value2"); + + boolean hasResultSet = ps.execute(); + + if (!hasResultSet) { + int updateCount = ps.getUpdateCount(); + // This should return 2 (for 2 inserted rows), not 1 + assertEquals(2, updateCount, "Update count should be 2 for INSERT with 2 values, but got: " + updateCount); + } else { + fail("Expected update count, but got ResultSet instead"); + } + } + TestUtils.dropTriggerIfExists(testTrigger, stmt); + TestUtils.dropTableIfExists(testTableB, stmt); + TestUtils.dropTableIfExists(testTableA, stmt); + } + } + @AfterEach public void terminate() { try (Connection con = getConnection(); Statement stmt = con.createStatement()) {