Skip to content

Commit d623cff

Browse files
committed
The statement preparation and binding for both output buffers now live in
a shared helper in AbstractOutputBuffer. The helper closes the statement if binding fails, so it no longer leaks on that error path.
1 parent db1e358 commit d623cff

4 files changed

Lines changed: 38 additions & 10 deletions

File tree

‎src/main/java/org/utplsql/api/outputBuffer/AbstractOutputBuffer.java‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.utplsql.api.outputBuffer;
22

3+
import oracle.jdbc.OracleTypes;
34
import org.utplsql.api.reporter.Reporter;
45

56
import java.io.PrintStream;
@@ -79,6 +80,27 @@ public void printAvailable(Connection conn, List<PrintStream> printStreams) thro
7980

8081
protected abstract CallableStatement getLinesCursorStatement(Connection conn) throws SQLException;
8182

83+
/**
84+
* Prepares the given PL/SQL block and binds the :reporter_id and :lines_cursor parameters.
85+
* The returned statement must be closed by the caller. It is closed here if binding fails.
86+
*
87+
* @param conn DB connection
88+
* @param plsql PL/SQL block using the :reporter_id and :lines_cursor bind variables
89+
* @return the prepared statement
90+
* @throws SQLException any sql errors
91+
*/
92+
protected CallableStatement prepareLinesCursorStatement(Connection conn, String plsql) throws SQLException {
93+
CallableStatement cstmt = conn.prepareCall(plsql);
94+
try {
95+
cstmt.setString("reporter_id", reporter.getId());
96+
cstmt.registerOutParameter("lines_cursor", OracleTypes.CURSOR);
97+
return cstmt;
98+
} catch (SQLException | RuntimeException e) {
99+
cstmt.close();
100+
throw e;
101+
}
102+
}
103+
82104
/**
83105
* Print the lines as soon as they are produced and call the callback passing the new line.
84106
*

‎src/main/java/org/utplsql/api/outputBuffer/CompatibilityOutputBufferPre310.java‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.utplsql.api.outputBuffer;
22

3-
import oracle.jdbc.OracleTypes;
43
import org.utplsql.api.reporter.Reporter;
54

65
import java.sql.CallableStatement;
@@ -20,9 +19,6 @@ class CompatibilityOutputBufferPre310 extends AbstractOutputBuffer {
2019

2120
@Override
2221
protected CallableStatement getLinesCursorStatement(Connection conn) throws SQLException {
23-
CallableStatement cstmt = conn.prepareCall("begin :lines_cursor := ut_output_buffer.get_lines_cursor(:reporter_id); end;");
24-
cstmt.registerOutParameter("lines_cursor", OracleTypes.CURSOR);
25-
cstmt.setString("reporter_id", getReporter().getId());
26-
return cstmt;
22+
return prepareLinesCursorStatement(conn, "begin :lines_cursor := ut_output_buffer.get_lines_cursor(:reporter_id); end;");
2723
}
2824
}

‎src/main/java/org/utplsql/api/outputBuffer/DefaultOutputBuffer.java‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.utplsql.api.outputBuffer;
22

3-
import oracle.jdbc.OracleTypes;
43
import org.utplsql.api.reporter.Reporter;
54

65
import java.sql.CallableStatement;
@@ -33,9 +32,6 @@ protected CallableStatement getLinesCursorStatement(Connection conn) throws SQLE
3332
" l_rep.set_reporter_id(:reporter_id); " +
3433
" :lines_cursor := l_rep.get_lines_cursor(); " +
3534
"end;";
36-
CallableStatement cstmt = conn.prepareCall(plsql);
37-
cstmt.setString("reporter_id", reporter.getId());
38-
cstmt.registerOutParameter("lines_cursor", OracleTypes.CURSOR);
39-
return cstmt;
35+
return prepareLinesCursorStatement(conn, plsql);
4036
}
4137
}

‎src/test/java/org/utplsql/api/outputBuffer/DefaultOutputBufferTest.java‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
import static org.hamcrest.MatcherAssert.assertThat;
1515
import static org.hamcrest.Matchers.contains;
16+
import static org.hamcrest.Matchers.sameInstance;
17+
import static org.junit.jupiter.api.Assertions.assertThrows;
1618
import static org.mockito.ArgumentMatchers.anyString;
1719
import static org.mockito.Mockito.*;
1820

@@ -63,4 +65,16 @@ void printAvailableWorksWithNonOracleCallableStatement() throws SQLException {
6365
verify(callableStatement).execute();
6466
verify(callableStatement).close();
6567
}
68+
69+
@Test
70+
void statementIsClosedWhenBindingFails() throws SQLException {
71+
SQLException bindError = new SQLException("bind failed");
72+
doThrow(bindError).when(callableStatement).setString(anyString(), anyString());
73+
74+
SQLException thrown = assertThrows(SQLException.class, () -> new DefaultOutputBuffer(reporter).fetchAll(connection));
75+
76+
assertThat(thrown, sameInstance(bindError));
77+
verify(callableStatement).close();
78+
verify(callableStatement, never()).execute();
79+
}
6680
}

0 commit comments

Comments
 (0)