diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java b/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java index d76e9918d4..4d656b0e40 100644 --- a/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java +++ b/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java @@ -847,6 +847,19 @@ private void setSocketTimeout(int millis) throws PSQLException { break; + case 'S': // Parameter Status + try { + receiveParameterStatus(); + } catch (SQLException e) { + if (error == null) { + error = e; + } else { + error.setNextException(e); + } + endQuery = true; + } + break; + default: throw new PSQLException(GT.tr("Unknown Response Type {0}.", (char) c), PSQLState.CONNECTION_FAILURE); diff --git a/pgjdbc/src/test/java/org/postgresql/jdbc/LargeObjectManagerTest.java b/pgjdbc/src/test/java/org/postgresql/jdbc/LargeObjectManagerTest.java new file mode 100644 index 0000000000..7a291f16bc --- /dev/null +++ b/pgjdbc/src/test/java/org/postgresql/jdbc/LargeObjectManagerTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021, PostgreSQL Global Development Group + * See the LICENSE file in the project root for more information. + */ + +package org.postgresql.jdbc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import org.postgresql.largeobject.LargeObjectManager; +import org.postgresql.test.TestUtil; +import org.postgresql.util.PSQLException; +import org.postgresql.util.PSQLState; + +import org.junit.jupiter.api.Test; + +import java.sql.Statement; + +class LargeObjectManagerTest { + + /* + * It is possible for PostgreSQL to send a ParameterStatus message after an ErrorResponse + * Receiving such a message should not lead to an invalid connection state + * See https://github.com/pgjdbc/pgjdbc/issues/2237 + */ + @Test + public void testOpenWithErrorAndSubsequentParameterStatusMessageShouldLeaveConnectionInUsableStateAndUpdateParameterStatus() throws Exception { + try (PgConnection con = (PgConnection) TestUtil.openDB()) { + con.setAutoCommit(false); + String originalApplicationName = con.getParameterStatus("application_name"); + try (Statement statement = con.createStatement()) { + statement.execute("begin;"); + // Set transaction application_name to trigger ParameterStatus message after error + // https://www.postgresql.org/docs/14/protocol-flow.html#PROTOCOL-ASYNC + String updatedApplicationName = "LargeObjectManagerTest-application-name"; + statement.execute("set application_name to '" + updatedApplicationName + "'"); + + LargeObjectManager loManager = con.getLargeObjectAPI(); + try { + loManager.open(0, false); + fail("Succeeded in opening a non-existent large object"); + } catch (PSQLException e) { + assertEquals(PSQLState.UNDEFINED_OBJECT.getState(), e.getSQLState()); + } + + // Should be reset to original application name + assertEquals(originalApplicationName, con.getParameterStatus("application_name")); + } + } + } +}