diff --git a/fdb-relational-grpc/src/main/proto/grpc/relational/jdbc/v1/jdbc.proto b/fdb-relational-grpc/src/main/proto/grpc/relational/jdbc/v1/jdbc.proto index 35f90ca414..e1e12e2c1f 100644 --- a/fdb-relational-grpc/src/main/proto/grpc/relational/jdbc/v1/jdbc.proto +++ b/fdb-relational-grpc/src/main/proto/grpc/relational/jdbc/v1/jdbc.proto @@ -121,6 +121,7 @@ message TransactionalRequest { InsertRequest insertRequest = 2; CommitRequest commitRequest = 3; RollbackRequest rollbackRequest = 4; + EnableAutoCommitRequest enableAutoCommitRequest = 5; } } @@ -130,7 +131,10 @@ message TransactionalResponse { InsertResponse insertResponse = 2; CommitResponse commitResponse = 3; RollbackResponse rollbackResponse = 4; + google.protobuf.Any errorResponse = 5; + + EnableAutoCommitResponse enableAutoCommitResponse = 6; } } @@ -162,6 +166,12 @@ message RollbackRequest { message RollbackResponse { } +message EnableAutoCommitRequest { +} + +message EnableAutoCommitResponse { +} + message DatabaseMetaDataRequest {} message DatabaseMetaDataResponse { string url = 1; diff --git a/fdb-relational-jdbc/src/main/java/com/apple/foundationdb/relational/jdbc/JDBCRelationalConnection.java b/fdb-relational-jdbc/src/main/java/com/apple/foundationdb/relational/jdbc/JDBCRelationalConnection.java index 1fef8d7109..b99a621417 100644 --- a/fdb-relational-jdbc/src/main/java/com/apple/foundationdb/relational/jdbc/JDBCRelationalConnection.java +++ b/fdb-relational-jdbc/src/main/java/com/apple/foundationdb/relational/jdbc/JDBCRelationalConnection.java @@ -31,6 +31,7 @@ import com.apple.foundationdb.relational.jdbc.grpc.GrpcSQLExceptionUtil; import com.apple.foundationdb.relational.jdbc.grpc.v1.CommitRequest; import com.apple.foundationdb.relational.jdbc.grpc.v1.DatabaseMetaDataRequest; +import com.apple.foundationdb.relational.jdbc.grpc.v1.EnableAutoCommitRequest; import com.apple.foundationdb.relational.jdbc.grpc.v1.InsertRequest; import com.apple.foundationdb.relational.jdbc.grpc.v1.InsertResponse; import com.apple.foundationdb.relational.jdbc.grpc.v1.JDBCServiceGrpc; @@ -345,7 +346,23 @@ public void setAutoCommit(boolean autoCommit) throws SQLException { this.autoCommit = false; } else { // commit any remaining work - commit(); + try { + TransactionalRequest.Builder transactionRequest = TransactionalRequest.newBuilder() + .setEnableAutoCommitRequest(EnableAutoCommitRequest.newBuilder().build()); + // wait here for response + final TransactionalResponse response = serverConnection.sendRequest(transactionRequest.build()); + checkForResponseError(response); + if (!response.hasEnableAutoCommitResponse()) { + throw new JdbcConnectionException("Wrong kind of response received, expected EnableAutoCommitResponse"); + } + } catch (StatusRuntimeException statusRuntimeException) { + final SQLException sqlException = toSQLException(statusRuntimeException); + if (sqlException != null) { + throw sqlException; + } else { + throw statusRuntimeException; + } + } this.autoCommit = true; serverConnection.close(); serverConnection = null; diff --git a/fdb-relational-jdbc/src/test/java/com/apple/foundationdb/relational/jdbc/JDBCAutoCommitTest.java b/fdb-relational-jdbc/src/test/java/com/apple/foundationdb/relational/jdbc/JDBCAutoCommitTest.java index 8360757f8a..564d0afdba 100644 --- a/fdb-relational-jdbc/src/test/java/com/apple/foundationdb/relational/jdbc/JDBCAutoCommitTest.java +++ b/fdb-relational-jdbc/src/test/java/com/apple/foundationdb/relational/jdbc/JDBCAutoCommitTest.java @@ -69,7 +69,7 @@ public void tearDown() throws Exception { * Run a test with the default (auto-commit on) for sanity. */ @Test - void autoCommitOn() throws SQLException, IOException { + void autoCommitOn() throws SQLException { try (RelationalConnection connection = DriverManager.getConnection(getTestDbUri()).unwrap(RelationalConnection.class)) { try (RelationalStatement statement = connection.createStatement()) { insertOneRow(statement); @@ -80,6 +80,69 @@ void autoCommitOn() throws SQLException, IOException { } } + @Test + void autoCommitWithNoTransactionInBetween() throws SQLException { + try (RelationalConnection connection = DriverManager.getConnection(getTestDbUri()).unwrap(RelationalConnection.class)) { + connection.setAutoCommit(false); + connection.setAutoCommit(true); + } + } + + @Test + void autoCommitStayOn() throws SQLException { + try (RelationalConnection connection = DriverManager.getConnection(getTestDbUri()).unwrap(RelationalConnection.class)) { + connection.setAutoCommit(true); + } + } + + @Test + void autoCommitStayOff() throws SQLException { + try (RelationalConnection connection = DriverManager.getConnection(getTestDbUri()).unwrap(RelationalConnection.class)) { + connection.setAutoCommit(false); + connection.setAutoCommit(false); + } + } + + @Test + void commitEnableAutoCommit() throws SQLException { + try (RelationalConnection connection = DriverManager.getConnection(getTestDbUri()).unwrap(RelationalConnection.class)) { + connection.setAutoCommit(false); + + try (RelationalStatement statement = connection.createStatement()) { + insertOneRow(statement); + } + connection.commit(); + connection.setAutoCommit(true); + try (RelationalStatement statement = connection.createStatement()) { + insertOneRow(statement, 101); + } + try (RelationalStatement statement = connection.createStatement()) { + RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM test_table"); + assertNextResult(resultSet, 100, "one hundred"); + assertNextResult(resultSet, 101, "one hundred"); + assertNoNextResult(resultSet); + } + } + } + + @Test + void rollbackThenEnableAutoCommit() throws SQLException { + try (RelationalConnection connection = DriverManager.getConnection(getTestDbUri()).unwrap(RelationalConnection.class)) { + connection.setAutoCommit(false); + + try (RelationalStatement statement = connection.createStatement()) { + insertOneRow(statement); + } + connection.rollback(); + connection.setAutoCommit(true); + try (RelationalStatement statement = connection.createStatement()) { + RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM test_table"); + assertNoNextResult(resultSet); + assertNoNextResult(resultSet); + } + } + } + /** * Run a test with commit and then read. */ @@ -148,7 +211,7 @@ void revertToAutoCommitOn() throws Exception { try (RelationalStatement statement = connection.createStatement()) { insertOneRow(statement); - connection.setAutoCommit(true); + connection.setAutoCommit(true); // this should commit RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM test_table"); assertNextResult(resultSet, 100, "one hundred"); @@ -323,8 +386,12 @@ protected String getHostPort() { } private static void insertOneRow(final RelationalStatement statement) throws SQLException { + insertOneRow(statement, 100); + } + + private static void insertOneRow(final RelationalStatement statement, int restNo) throws SQLException { RelationalStruct insert = JDBCRelationalStruct.newBuilder() - .addLong("REST_NO", 100) + .addLong("REST_NO", restNo) .addString("NAME", "one hundred") .build(); int res = statement.executeInsert("TEST_TABLE", insert); diff --git a/fdb-relational-server/src/main/java/com/apple/foundationdb/relational/server/FRL.java b/fdb-relational-server/src/main/java/com/apple/foundationdb/relational/server/FRL.java index e77dcd0dfe..36b8d67e7c 100644 --- a/fdb-relational-server/src/main/java/com/apple/foundationdb/relational/server/FRL.java +++ b/fdb-relational-server/src/main/java/com/apple/foundationdb/relational/server/FRL.java @@ -371,6 +371,11 @@ public void transactionalRollback(TransactionalToken token) throws SQLException token.getConnection().rollback(); } + public void enableAutoCommit(TransactionalToken token) throws SQLException { + assertValidToken(token); + token.getConnection().setAutoCommit(true); + } + public void transactionalClose(TransactionalToken token) throws SQLException { if (token != null && !token.expired()) { token.close(); diff --git a/fdb-relational-server/src/main/java/com/apple/foundationdb/relational/server/jdbc/v1/TransactionRequestHandler.java b/fdb-relational-server/src/main/java/com/apple/foundationdb/relational/server/jdbc/v1/TransactionRequestHandler.java index 9ff6a9e494..7d7d26a937 100644 --- a/fdb-relational-server/src/main/java/com/apple/foundationdb/relational/server/jdbc/v1/TransactionRequestHandler.java +++ b/fdb-relational-server/src/main/java/com/apple/foundationdb/relational/server/jdbc/v1/TransactionRequestHandler.java @@ -26,6 +26,7 @@ import com.apple.foundationdb.relational.jdbc.TypeConversion; import com.apple.foundationdb.relational.jdbc.grpc.GrpcSQLExceptionUtil; import com.apple.foundationdb.relational.jdbc.grpc.v1.CommitResponse; +import com.apple.foundationdb.relational.jdbc.grpc.v1.EnableAutoCommitResponse; import com.apple.foundationdb.relational.jdbc.grpc.v1.InsertRequest; import com.apple.foundationdb.relational.jdbc.grpc.v1.InsertResponse; import com.apple.foundationdb.relational.jdbc.grpc.v1.RollbackResponse; @@ -110,8 +111,16 @@ public void onNext(final TransactionalRequest transactionRequest) { logger.info("Handling rollback request"); frl.transactionalRollback(transactionalToken); responseBuilder.setRollbackResponse(RollbackResponse.newBuilder().build()); + } else if (transactionRequest.hasEnableAutoCommitRequest()) { + logger.info("Enabling autoCommit"); + // we don't actually call setAutoCommit(false) until an operation happens, so if there is no token + // there's no connection that needs updating + if (transactionalToken != null) { + frl.enableAutoCommit(transactionalToken); + } + responseBuilder.setEnableAutoCommitResponse(EnableAutoCommitResponse.newBuilder().build()); } else { - throw new IllegalArgumentException("Unknown transactional request type in" + transactionRequest); + throw new IllegalArgumentException("Unknown transactional request type: " + transactionRequest); } responseObserver.onNext(responseBuilder.build()); } catch (SQLException e) {