Repository navigation
Conversation
This comment has been minimized.
This comment has been minimized.
| .withQuery(query) | ||
| .withExistingStatement(this) | ||
| .build() | ||
| .prepareAndExecute(callback); |
There was a problem hiding this comment.
Perhaps a nitpick, but if we run multiple queries with the same statement using execute we will only close one of the handles in the server.
Code for repro:
try (Connection connection = DriverManager.getConnection(jdbcUrl, properties);
Statement statement = connection.createStatement()) {
for (int i = 1; i <= n; i++) {
String sql = "SELECT " + i;
log("Executing: " + sql);
boolean isResultSet = statement.execute(sql);
if (isResultSet) {
try (ResultSet rs = statement.getResultSet()) {
// consume result set
}
} else {
System.out.println("Updated rows: " + statement.getUpdateCount());
}
}
}
A new prepared statement will be created on each query, but only one ClosePreparedStatement is sent to the server. I believe most Flight SQL servers are stateless and this won't be problematic in that scenario, but in case the server is holding some resources associated with the handle(s) of the prepared statement(s) that aren't closed there will be a leak.
Could we send the close after execution?
There was a problem hiding this comment.
Good catch. This issue existed before the refactor. I've created #1129 to be addressed separately
e49fea0 to
743dc87
Compare
a667705 to
3a77376
Compare
|
@xborder thanks for the update! I will do the review. |
* reduced number of requests for Statement * move meta orchestration * detect prepared statement
This reverts commit b57550f.
Restore source-compatible exception declarations and map prepared-statement transition failures to SQLException.
Dispatch Avatica operations through package-private hooks and guard the public statement API surface.
Keep FlightEndpointDataQueue fail-fast while normalizing cancellation races inside the JDBC result-set path.
146cd30 to
b7bd7c5
Compare
| private int metadataResultSetCount; | ||
| // Track result sets and statements owned by this connection so close() can release them. | ||
| private Map<Integer, ArrowFlightJdbcFlightStreamResultSet> metadataResultSetMap = new HashMap<>(); | ||
| private final Set<AvaticaStatement> statementOwners = |
There was a problem hiding this comment.
I wonder, could we separate this change out into its own PR so it shows up more clearly in the changelog/history and is easier to review?
There was a problem hiding this comment.
Albeit if it can't be separated then that's fine
There was a problem hiding this comment.
This is to ensure that we close the result sets for the original statements when the connection is closed or reset, even if their entries in Avatica’s statement map have been replaced.
| final PrepareCallback callback) | ||
| throws SQLException { | ||
|
|
||
| return ArrowFlightPreparedStatement.builder(getConnection()) |
There was a problem hiding this comment.
Calling Statement.execute(String) again on the same statement leaks the previous server-side prepared statement.
On the second call, statementMap holds the internal ArrowFlightPreparedStatement created by the first call, so ArrowFlightMetaImpl.prepareAndExecute routes here. We build a new prepared statement under the same handle, and its constructor replaces the statementMap entry, but this.preparedStatement is never closed. No ClosePreparedStatement is sent for it. A loop of N execute() calls leaves N-1 prepared statements open on the server, and stmt.close() only closes the last one.
The old statementHandlePreparedStatementMap.put(...) dropped the previous entry the same way, so this is not a regression. Since this method is new, could we close the previous one once the replacement is built?
final ArrowFlightPreparedStatement replacement =
ArrowFlightPreparedStatement.builder(getConnection())
.withQuery(query)
.withExistingStatement(this)
.build();
closePreparedResources();
return replacement.prepareAndExecuteInternal(callback);A test that calls execute() a few times on one statement and checks how many prepared statements the mock producer closed would cover this.
There was a problem hiding this comment.
I had already identified this issue. It already exists on main. I suggest addressing it in a follow-up PR after this one merges.
| } catch (final FlightRuntimeException e) { | ||
| throw new SQLException(e.getMessage(), e); | ||
| } | ||
| final Signature signature = |
There was a problem hiding this comment.
If anything throws between prepare() and the end of build(), the server side prepared statement is never cloud. One concrete case: a parameter schema with a ListView, LargeListView or RunEndEncoded field makes ConvertUtils.convertArrowFieldsToAvaticaParameters throw UnsupportedOperationException from buildSignature.
The old prepareForHandle had the same gap. Now that build() owns the whole lifecycle, could we close it on failure?
final ArrowFlightPreparedStatement statement;
try {
final Signature signature =
ArrowFlightMetaImpl.buildSignature(
query,
preparedStatement.getDataSetSchema(),
preparedStatement.getParameterSchema(),
preparedStatement.isUpdate());
statement =
new ArrowFlightPreparedStatement(
connection,
generateHandle ? null : handle,
signature,
preparedStatement,
resultSetType,
resultSetConcurrency,
resultSetHoldability);
} catch (final Exception e) {
try {
preparedStatement.close();
} catch (final Exception closeException) {
e.addSuppressed(closeException);
}
throw e;
}Remove redundant API visibility and interface identity tests, retain the checked prepareStatement implementation, and clarify connection ownership comments.
testShouldInterruptFlightStreamsIfQueryIsCancelledMidQuerying called statement.cancel() right after starting the worker thread. It relied on the prepare round trip in executeQuery to make the cancel land before a result set existed, so only Avatica's cancel flag was set and next() threw "Statement canceled". Statement.executeQuery no longer prepares first, so the cancel can now land while the result set is executing and surface as "Error while executing SQL ...: exception while executing query: Statement canceled", which failed the RC Binaries job. Wait until executeQuery returns before cancelling, and replace the 300 ms sleep with a latch.
What Changed
Statement.executeQuery/executeUpdateavoiding thePreparedStatementrequest flowStatement.execute(String)still flows through PreparedStatementCommandStatementUpdateorCommandStatemenQuerywithout doing a server callArrowFlightPreparedStatementArrowFlightStatementwith specific logic to handleStatementsArrowFlightMetaImpl.ArrowFlightMetaImplwas left it focused on orchestrationCloses #62.
This PR was assisted by AI