Skip to content

Commit 81477ee

Browse files
authored
Fixed incorrect updateCount (#2013) (#2021)
* Fixed incorrect updateCount * Formatting changes * Additional testing and changes * Removed debug print line
1 parent 3d61735 commit 81477ee

File tree

2 files changed

+149
-2
lines changed

2 files changed

+149
-2
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2972,6 +2972,11 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th
29722972
if (null == batchCommand.batchException)
29732973
batchCommand.batchException = e;
29742974

2975+
if (batchCommand.batchException.getSQLState()
2976+
.equals(SQLState.STATEMENT_CANCELED.getSQLStateCode())) {
2977+
processBatch();
2978+
continue;
2979+
}
29752980
}
29762981

29772982
// In batch execution, we have a special update count

src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java

Lines changed: 144 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@
44
*/
55
package com.microsoft.sqlserver.jdbc.unit.statement;
66

7+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
78
import static org.junit.jupiter.api.Assertions.assertTrue;
89
import static org.junit.jupiter.api.Assertions.assertEquals;
910
import static org.junit.jupiter.api.Assertions.fail;
1011

1112
import java.lang.reflect.Field;
13+
import java.sql.BatchUpdateException;
14+
import java.sql.CallableStatement;
1215
import java.sql.Connection;
1316
import java.sql.PreparedStatement;
1417
import java.sql.ResultSet;
1518
import java.sql.SQLException;
1619
import java.sql.Statement;
20+
import java.util.Arrays;
1721

22+
import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement;
1823
import org.junit.jupiter.api.AfterAll;
1924
import org.junit.jupiter.api.BeforeAll;
2025
import org.junit.jupiter.api.Tag;
@@ -44,8 +49,86 @@
4449
@Tag(Constants.xAzureSQLDW)
4550
public class BatchExecutionTest extends AbstractTest {
4651

47-
static String ctstable1;
48-
static String ctstable2;
52+
private static String ctstable1;
53+
private static String ctstable2;
54+
private static String ctstable3;
55+
private static String ctstable4;
56+
private static String ctstable3Procedure1;
57+
58+
/**
59+
* This tests the updateCount when the error query does cause a SQL state HY008.
60+
*
61+
* @throws Exception
62+
*/
63+
@Test
64+
public void testBatchUpdateCountFalseOnFirstPstmtPrepexec() throws Exception {
65+
long[] expectedUpdateCount = {1, 1, 1, 1, -3, -3, -3, -3, -3, -3};
66+
testBatchUpdateCountWith(10, 6, false, "prepexec", expectedUpdateCount);
67+
}
68+
69+
/**
70+
* This tests the updateCount when the error query does cause a SQL state HY008.
71+
*
72+
* @throws Exception
73+
*/
74+
@Test
75+
public void testBatchUpdateCountTrueOnFirstPstmtPrepexec() throws Exception {
76+
long[] expectedUpdateCount = {1, 1, -3, -3, -3};
77+
testBatchUpdateCountWith(5, 4, true, "prepexec", expectedUpdateCount);
78+
}
79+
80+
/**
81+
* This tests the updateCount when the error query does cause a SQL state HY008.
82+
*
83+
* @throws Exception
84+
*/
85+
@Test
86+
public void testBatchUpdateCountFalseOnFirstPstmtSpPrepare() throws Exception {
87+
long[] expectedUpdateCount = {1, 1, 1, 1, -3, -3, -3, -3, -3, -3};
88+
testBatchUpdateCountWith(10, 6, false, "prepare", expectedUpdateCount);
89+
}
90+
91+
/**
92+
* This tests the updateCount when the error query does cause a SQL state HY008.
93+
*
94+
* @throws Exception
95+
*/
96+
@Test
97+
public void testBatchUpdateCountTrueOnFirstPstmtSpPrepare() throws Exception {
98+
long[] expectedUpdateCount = {1, 1, -3, -3, -3};
99+
testBatchUpdateCountWith(5, 4, true, "prepare", expectedUpdateCount);
100+
}
101+
102+
/**
103+
* This tests the updateCount when the error query does not cause a SQL state HY008.
104+
*
105+
* @throws Exception
106+
*/
107+
@Test
108+
public void testBatchUpdateCount() throws Exception {
109+
long[] expectedUpdateCount = {1, 1, 1, 1, -3, 1, 1, 1, 1, 1};
110+
111+
try (SQLServerConnection connection = PrepUtil.getConnection(connectionString)) {
112+
try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(
113+
"insert into " + AbstractSQLGenerator.escapeIdentifier(ctstable4) + " values(?)")) {
114+
for (int i = 1; i <= 10; i++) {
115+
if (i == 5) {
116+
pstmt.setInt(1, -1);
117+
} else {
118+
pstmt.setInt(1, i);
119+
}
120+
pstmt.addBatch();
121+
}
122+
123+
try {
124+
pstmt.executeBatch();
125+
} catch (BatchUpdateException e) {
126+
assertArrayEquals(expectedUpdateCount, e.getLargeUpdateCounts(),
127+
"Actual: " + Arrays.toString(e.getLargeUpdateCounts()));
128+
}
129+
}
130+
}
131+
}
49132

50133
/**
51134
* testAddBatch1 and testExecutionBatch one looks similar except for the parameters being passed for select query.
@@ -139,6 +222,35 @@ public void testExecuteBatch1UseBulkCopyAPI() {
139222
testExecuteBatch1Internal("BulkCopy");
140223
}
141224

225+
private void testBatchUpdateCountWith(int numOfInserts, int errorQueryIndex,
226+
boolean prepareOnFirstPreparedStatement, String prepareMethod,
227+
long[] expectedUpdateCount) throws Exception {
228+
try (SQLServerConnection connection = PrepUtil.getConnection(connectionString)) {
229+
connection.setEnablePrepareOnFirstPreparedStatementCall(prepareOnFirstPreparedStatement);
230+
connection.setPrepareMethod(prepareMethod);
231+
try (CallableStatement cstmt = connection.prepareCall(
232+
AbstractSQLGenerator.escapeIdentifier(ctstable3Procedure1) + " @duration=?, @value=?")) {
233+
cstmt.setQueryTimeout(7);
234+
for (int i = 1; i <= numOfInserts; i++) {
235+
if (i == errorQueryIndex) {
236+
cstmt.setString(1, "00:00:14");
237+
} else {
238+
cstmt.setString(1, "00:00:00");
239+
}
240+
cstmt.setInt(2, i);
241+
cstmt.addBatch();
242+
}
243+
244+
try {
245+
cstmt.executeBatch();
246+
} catch (BatchUpdateException e) {
247+
assertArrayEquals(expectedUpdateCount, e.getLargeUpdateCounts(),
248+
"Actual: " + Arrays.toString(e.getLargeUpdateCounts()));
249+
}
250+
}
251+
}
252+
}
253+
142254
private void testExecuteBatch1Internal(String mode) {
143255
int i = 0;
144256
int retValue[] = {0, 0, 0};
@@ -193,6 +305,17 @@ private void testExecuteBatch1Internal(String mode) {
193305
}
194306
}
195307

308+
private static void createProcedure() throws SQLException {
309+
String sql1 = "CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(ctstable3Procedure1) + "\n"
310+
+ "@value int,\n" + "@duration varchar(8)\n" + "AS\n" + "BEGIN\n" + "WAITFOR DELAY @duration;\n"
311+
+ "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(ctstable3) + " VALUES (@value);\n" + "END";
312+
313+
try (Connection connection = PrepUtil.getConnection(connectionString);
314+
Statement stmt = (SQLServerStatement) connection.createStatement()) {
315+
stmt.execute(sql1);
316+
} ;
317+
}
318+
196319
private static void createTable() throws SQLException {
197320
try (Connection connection = PrepUtil.getConnection(connectionString + ";columnEncryptionSetting=Enabled;");
198321
Statement stmt = (SQLServerStatement) connection.createStatement()) {
@@ -201,8 +324,13 @@ private static void createTable() throws SQLException {
201324
String sql2 = "create table " + AbstractSQLGenerator.escapeIdentifier(ctstable2)
202325
+ " (KEY_ID int, COF_NAME varchar(32), PRICE float, TYPE_ID int, primary key(KEY_ID), foreign key(TYPE_ID) references "
203326
+ AbstractSQLGenerator.escapeIdentifier(ctstable1) + ")";
327+
String sql3 = "create table " + AbstractSQLGenerator.escapeIdentifier(ctstable3) + "(C1 int)";
328+
String sql4 = "create table " + AbstractSQLGenerator.escapeIdentifier(ctstable4)
329+
+ "(C1 int check (C1 > 0))";
204330
stmt.execute(sql1);
205331
stmt.execute(sql2);
332+
stmt.execute(sql3);
333+
stmt.execute(sql4);
206334

207335
String sqlin2 = "insert into " + AbstractSQLGenerator.escapeIdentifier(ctstable1)
208336
+ " values (1,'COFFEE-Desc')";
@@ -315,15 +443,29 @@ public static void testSetup() throws TestAbortedException, Exception {
315443

316444
ctstable1 = RandomUtil.getIdentifier("ctstable1");
317445
ctstable2 = RandomUtil.getIdentifier("ctstable2");
446+
ctstable3 = RandomUtil.getIdentifier("ctstable3");
447+
ctstable4 = RandomUtil.getIdentifier("ctstable4");
448+
ctstable3Procedure1 = RandomUtil.getIdentifier("ctstable3Procedure1");
318449

319450
dropTable();
320451
createTable();
452+
453+
dropProcedure();
454+
createProcedure();
455+
}
456+
457+
private static void dropProcedure() throws SQLException {
458+
try (Statement stmt = connection.createStatement()) {
459+
TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(ctstable3Procedure1), stmt);
460+
}
321461
}
322462

323463
private static void dropTable() throws SQLException {
324464
try (Statement stmt = connection.createStatement()) {
325465
TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(ctstable2), stmt);
326466
TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(ctstable1), stmt);
467+
TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(ctstable3), stmt);
468+
TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(ctstable4), stmt);
327469
}
328470
}
329471

0 commit comments

Comments
 (0)