@@ -1028,15 +1028,156 @@ public void testReExecutionClosesPreviousResultSetWithoutServerHandleClose() thr
10281028 // First execution
10291029 statement .executeQuery (STATEMENT );
10301030
1031- // Second execution — previous ResultSet is closed per JDBC spec.
1032- // Server handle is NOT explicitly closed (server manages handle lifecycle ).
1031+ // Second execution — previous ResultSet closed. No server close because
1032+ // statementId is null in this mock setup (mock doesn't call setStatementId ).
10331033 statement .executeQuery (STATEMENT );
10341034
10351035 verify (firstResult , times (1 )).close ();
10361036 verify (client , never ()).closeStatement (any (StatementId .class ));
10371037 assertEquals (secondResult , statement .getResultSet ());
10381038 }
10391039
1040+ @ Test
1041+ public void testReExecutionClosesServerOperation () throws Exception {
1042+ IDatabricksConnectionContext connectionContext =
1043+ DatabricksConnectionContext .parse (JDBC_URL , new Properties ());
1044+ DatabricksConnection connection = new DatabricksConnection (connectionContext , client );
1045+ DatabricksStatement statement = new DatabricksStatement (connection );
1046+
1047+ DatabricksResultSet firstResult = mock (DatabricksResultSet .class );
1048+ DatabricksResultSet secondResult = mock (DatabricksResultSet .class );
1049+
1050+ when (client .executeStatement (
1051+ eq (STATEMENT ),
1052+ eq (new Warehouse (WAREHOUSE_ID )),
1053+ eq (new HashMap <>()),
1054+ eq (StatementType .QUERY ),
1055+ any (IDatabricksSession .class ),
1056+ eq (statement ),
1057+ any ()))
1058+ .thenReturn (firstResult )
1059+ .thenReturn (secondResult );
1060+
1061+ // First execution
1062+ statement .executeQuery (STATEMENT );
1063+ // Simulate server setting the statementId (normally done inside executeStatement)
1064+ StatementId firstStatementId = new StatementId ("first-stmt-id" );
1065+ statement .setStatementId (firstStatementId );
1066+
1067+ // Second execution — should close the first server operation
1068+ statement .executeQuery (STATEMENT );
1069+
1070+ verify (client , times (1 )).closeStatement (eq (firstStatementId ));
1071+ verify (firstResult , times (1 )).close ();
1072+ assertEquals (secondResult , statement .getResultSet ());
1073+ }
1074+
1075+ @ Test
1076+ public void testReExecutionSkipsServerCloseForDirectResults () throws Exception {
1077+ IDatabricksConnectionContext connectionContext =
1078+ DatabricksConnectionContext .parse (JDBC_URL , new Properties ());
1079+ DatabricksConnection connection = new DatabricksConnection (connectionContext , client );
1080+ DatabricksStatement statement = new DatabricksStatement (connection );
1081+
1082+ DatabricksResultSet firstResult = mock (DatabricksResultSet .class );
1083+ DatabricksResultSet secondResult = mock (DatabricksResultSet .class );
1084+
1085+ when (client .executeStatement (
1086+ eq (STATEMENT ),
1087+ eq (new Warehouse (WAREHOUSE_ID )),
1088+ eq (new HashMap <>()),
1089+ eq (StatementType .QUERY ),
1090+ any (IDatabricksSession .class ),
1091+ eq (statement ),
1092+ any ()))
1093+ .thenReturn (firstResult )
1094+ .thenReturn (secondResult );
1095+
1096+ // First execution with direct results (server already closed the operation)
1097+ statement .executeQuery (STATEMENT );
1098+ statement .setStatementId (new StatementId ("direct-stmt-id" ));
1099+ statement .markDirectResultsReceived ();
1100+
1101+ // Second execution — should NOT close server operation (already closed by server)
1102+ statement .executeQuery (STATEMENT );
1103+
1104+ verify (client , never ()).closeStatement (any (StatementId .class ));
1105+ verify (firstResult , times (1 )).close ();
1106+ }
1107+
1108+ @ Test
1109+ public void testReExecutionHandlesCloseFailureGracefully () throws Exception {
1110+ IDatabricksConnectionContext connectionContext =
1111+ DatabricksConnectionContext .parse (JDBC_URL , new Properties ());
1112+ DatabricksConnection connection = new DatabricksConnection (connectionContext , client );
1113+ DatabricksStatement statement = new DatabricksStatement (connection );
1114+
1115+ DatabricksResultSet firstResult = mock (DatabricksResultSet .class );
1116+ DatabricksResultSet secondResult = mock (DatabricksResultSet .class );
1117+ StatementId firstStatementId = new StatementId ("failing-stmt-id" );
1118+
1119+ // closeStatement throws (e.g., operation already expired on server)
1120+ doThrow (new DatabricksSQLException ("Operation not found" , "HY000" ))
1121+ .when (client )
1122+ .closeStatement (eq (firstStatementId ));
1123+
1124+ when (client .executeStatement (
1125+ eq (STATEMENT ),
1126+ eq (new Warehouse (WAREHOUSE_ID )),
1127+ eq (new HashMap <>()),
1128+ eq (StatementType .QUERY ),
1129+ any (IDatabricksSession .class ),
1130+ eq (statement ),
1131+ any ()))
1132+ .thenReturn (firstResult )
1133+ .thenReturn (secondResult );
1134+
1135+ statement .executeQuery (STATEMENT );
1136+ statement .setStatementId (firstStatementId );
1137+
1138+ // Re-execution should succeed even though closing previous operation failed
1139+ assertDoesNotThrow (() -> statement .executeQuery (STATEMENT ));
1140+ assertEquals (secondResult , statement .getResultSet ());
1141+ }
1142+
1143+ @ Test
1144+ public void testReExecutionHandlesTransportErrorGracefully () throws Exception {
1145+ IDatabricksConnectionContext connectionContext =
1146+ DatabricksConnectionContext .parse (JDBC_URL , new Properties ());
1147+ DatabricksConnection connection = new DatabricksConnection (connectionContext , client );
1148+ DatabricksStatement statement = new DatabricksStatement (connection );
1149+
1150+ DatabricksResultSet firstResult = mock (DatabricksResultSet .class );
1151+ DatabricksResultSet secondResult = mock (DatabricksResultSet .class );
1152+ StatementId firstStatementId = new StatementId ("transport-error-stmt-id" );
1153+
1154+ // closeStatement throws a transport-level error (e.g., unexpected server response,
1155+ // corrupted framed transport). This is the scarier failure mode — not just "not found"
1156+ // but a low-level I/O error that could corrupt shared transport state.
1157+ doThrow (new RuntimeException ("HTTP request failed by code: 500, unexpected response" ))
1158+ .when (client )
1159+ .closeStatement (eq (firstStatementId ));
1160+
1161+ when (client .executeStatement (
1162+ eq (STATEMENT ),
1163+ eq (new Warehouse (WAREHOUSE_ID )),
1164+ eq (new HashMap <>()),
1165+ eq (StatementType .QUERY ),
1166+ any (IDatabricksSession .class ),
1167+ eq (statement ),
1168+ any ()))
1169+ .thenReturn (firstResult )
1170+ .thenReturn (secondResult );
1171+
1172+ statement .executeQuery (STATEMENT );
1173+ statement .setStatementId (firstStatementId );
1174+
1175+ // Re-execution must succeed even with transport-level close failure.
1176+ // The new execution creates a fresh server operation with a new statementId.
1177+ assertDoesNotThrow (() -> statement .executeQuery (STATEMENT ));
1178+ assertEquals (secondResult , statement .getResultSet ());
1179+ }
1180+
10401181 @ Test
10411182 public void testAsyncExecutionResetsStateFromPreviousSyncExecution () throws Exception {
10421183 IDatabricksConnectionContext connectionContext =
0 commit comments