Skip to content

Commit b64cb6a

Browse files
Fix potential connection leak when using jdbc storage and Scan operation fails because the target table doesn't exist (#2766)
Co-authored-by: Hiroyuki Yamada <[email protected]>
1 parent 1086d60 commit b64cb6a

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

core/src/main/java/com/scalar/db/storage/jdbc/JdbcDatabase.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,17 @@ public Scanner scan(Scan scan) throws ExecutionException {
104104
close(connection);
105105
throw new ExecutionException(
106106
CoreError.JDBC_ERROR_OCCURRED_IN_SELECTION.buildMessage(e.getMessage()), e);
107+
} catch (Exception e) {
108+
try {
109+
if (connection != null) {
110+
connection.rollback();
111+
}
112+
} catch (SQLException ex) {
113+
e.addSuppressed(ex);
114+
}
115+
116+
close(connection);
117+
throw e;
107118
}
108119
}
109120

@@ -173,6 +184,9 @@ public void mutate(List<? extends Mutation> mutations) throws ExecutionException
173184
close(connection);
174185
throw new ExecutionException(
175186
CoreError.JDBC_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e);
187+
} catch (Exception e) {
188+
close(connection);
189+
throw e;
176190
}
177191

178192
try {

core/src/test/java/com/scalar/db/storage/jdbc/JdbcDatabaseTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import static org.assertj.core.api.Assertions.assertThatThrownBy;
44
import static org.mockito.ArgumentMatchers.any;
5+
import static org.mockito.ArgumentMatchers.anyBoolean;
6+
import static org.mockito.Mockito.doThrow;
7+
import static org.mockito.Mockito.never;
58
import static org.mockito.Mockito.verify;
69
import static org.mockito.Mockito.when;
710

@@ -125,6 +128,25 @@ public void whenScanOperationExecutedAndScannerClosed_shouldCallJdbcService() th
125128
verify(connection).close();
126129
}
127130

131+
@Test
132+
public void
133+
whenScanOperationExecutedAndJdbcServiceThrowsIllegalArgumentException_shouldCloseConnectionAndThrowIllegalArgumentException()
134+
throws Exception {
135+
// Arrange
136+
Exception cause = new IllegalArgumentException("Table not found");
137+
// Simulate the table not found scenario.
138+
when(jdbcService.getScanner(any(), any())).thenThrow(cause);
139+
140+
// Act Assert
141+
assertThatThrownBy(
142+
() -> {
143+
Scan scan = new Scan(new Key("p1", "val")).forNamespace(NAMESPACE).forTable(TABLE);
144+
jdbcDatabase.scan(scan);
145+
})
146+
.isInstanceOf(IllegalArgumentException.class);
147+
verify(connection).close();
148+
}
149+
128150
@Test
129151
public void whenPutOperationExecuted_shouldCallJdbcService() throws Exception {
130152
// Arrange
@@ -330,4 +352,30 @@ public void mutate_withConflictError_shouldThrowRetriableExecutionException()
330352
.isInstanceOf(RetriableExecutionException.class);
331353
verify(connection).close();
332354
}
355+
356+
@Test
357+
public void mutate_WhenSettingAutoCommitFails_ShouldThrowExceptionAndCloseConnection()
358+
throws SQLException, ExecutionException {
359+
// Arrange
360+
Exception exception = new RuntimeException("Failed to set auto-commit");
361+
doThrow(exception).when(connection).setAutoCommit(anyBoolean());
362+
363+
// Act Assert
364+
assertThatThrownBy(
365+
() -> {
366+
Put put =
367+
new Put(new Key("p1", "val1"))
368+
.withValue("v1", "val2")
369+
.forNamespace(NAMESPACE)
370+
.forTable(TABLE);
371+
Delete delete =
372+
new Delete(new Key("p1", "val1")).forNamespace(NAMESPACE).forTable(TABLE);
373+
jdbcDatabase.mutate(Arrays.asList(put, delete));
374+
})
375+
.isEqualTo(exception);
376+
verify(connection).setAutoCommit(false);
377+
verify(jdbcService, never()).mutate(any(), any());
378+
verify(connection, never()).rollback();
379+
verify(connection).close();
380+
}
333381
}

0 commit comments

Comments
 (0)