[Git][java-team/libpostgresql-jdbc-java][upstream] New upstream version 42.7.7
Christoph Berg (@myon)
gitlab at salsa.debian.org
Fri Jun 13 14:31:49 BST 2025
Christoph Berg pushed to branch upstream at Debian Java Maintainers / libpostgresql-jdbc-java
Commits:
4ac37a14 by Christoph Berg at 2025-06-13T15:26:50+02:00
New upstream version 42.7.7
- - - - -
7 changed files:
- pom.xml
- src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
- src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
- src/main/java/org/postgresql/util/DriverInfo.java
- src/main/resources/META-INF/MANIFEST.MF
- + src/test/java/org/postgresql/test/core/ChannelBindingRequiredTest.java
- + src/test/java/org/postgresql/test/jdbc2/AutoRollbackTest.java
Changes:
=====================================
pom.xml
=====================================
@@ -8,7 +8,7 @@
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
- <version>42.7.6</version>
+ <version>42.7.7</version>
<packaging>jar</packaging>
<name>PostgreSQL JDBC Driver - JDBC 4.2</name>
<description>Java JDBC 4.2 (JRE 8+) driver for PostgreSQL database</description>
=====================================
src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
=====================================
@@ -702,6 +702,29 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
pgStream.flush();
}
+ private static String getAuthenticationMethodName(int authReq) {
+ switch (authReq) {
+ case AUTH_REQ_OK:
+ return "none";
+ case AUTH_REQ_PASSWORD:
+ return "password";
+ case AUTH_REQ_MD5:
+ return "md5";
+ case AUTH_REQ_GSS:
+ return "gss";
+ case AUTH_REQ_SSPI:
+ return "sspi";
+ case AUTH_REQ_SASL:
+ return "sasl";
+ case AUTH_REQ_SASL_CONTINUE:
+ return "sasl-continue";
+ case AUTH_REQ_SASL_FINAL:
+ return "sasl-final";
+ default:
+ return String.valueOf(authReq);
+ }
+ }
+
private static void doAuthentication(PGStream pgStream, String host, String user, Properties info) throws IOException, SQLException {
// Now get the response from the backend, either an error message
// or an authentication request
@@ -714,6 +737,8 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
// TODO: figure out how to deal with new protocols
int protocol = 3 << 16;
+ boolean saslHandshakeCompleted = false;
+
try {
authloop: while (true) {
int beresp = pgStream.receiveChar();
@@ -759,6 +784,23 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
// Get the type of request
int areq = pgStream.receiveInteger4();
+ if (ChannelBindingOption.of(info) == ChannelBindingOption.REQUIRE) {
+ if (areq == AUTH_REQ_OK) {
+ if (!saslHandshakeCompleted) {
+ throw new PSQLException(
+ GT.tr("Channel binding is required, but server skipped authentication. "
+ + "Channel binding is only supported with SCRAM authentication over encrypted connections."),
+ PSQLState.CONNECTION_REJECTED);
+ }
+ } else if (areq != AUTH_REQ_SASL && areq != AUTH_REQ_SASL_CONTINUE && areq != AUTH_REQ_SASL_FINAL) {
+ throw new PSQLException(
+ GT.tr("Channel binding is required, but server requested ''{0}'' authentication. "
+ + "Channel binding is only supported with SCRAM authentication over encrypted connections.",
+ getAuthenticationMethodName(areq)),
+ PSQLState.CONNECTION_REJECTED);
+ }
+ }
+
// Process the request.
switch (areq) {
case AUTH_REQ_MD5: {
@@ -914,6 +956,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
case AUTH_REQ_SASL_FINAL:
castNonNull(scramAuthenticator).handleAuthenticationSASLFinal(msgLen - 4 - 4);
+ saslHandshakeCompleted = true;
break;
case AUTH_REQ_OK:
=====================================
src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
=====================================
@@ -318,8 +318,6 @@ public class QueryExecutorImpl extends QueryExecutorBase {
switch (getPreferQueryMode()) {
case SIMPLE:
return flags | QUERY_EXECUTE_AS_SIMPLE;
- case EXTENDED:
- return flags & ~QUERY_EXECUTE_AS_SIMPLE;
default:
return flags;
}
=====================================
src/main/java/org/postgresql/util/DriverInfo.java
=====================================
@@ -16,13 +16,13 @@ public final class DriverInfo {
// Driver name
public static final String DRIVER_NAME = "PostgreSQL JDBC Driver";
public static final String DRIVER_SHORT_NAME = "PgJDBC";
- public static final String DRIVER_VERSION = "42.7.6";
+ public static final String DRIVER_VERSION = "42.7.7";
public static final String DRIVER_FULL_NAME = DRIVER_NAME + " " + DRIVER_VERSION;
// Driver version
public static final int MAJOR_VERSION = 42;
public static final int MINOR_VERSION = 7;
- public static final int PATCH_VERSION = 6;
+ public static final int PATCH_VERSION = 7;
// JDBC specification
public static final String JDBC_VERSION = "4.2";
=====================================
src/main/resources/META-INF/MANIFEST.MF
=====================================
@@ -1,7 +1,7 @@
Manifest-Version: 1.0
Bundle-License: BSD-2-Clause
Implementation-Title: PostgreSQL JDBC Driver
-Implementation-Version: 42.7.6
+Implementation-Version: 42.7.7
Specification-Vendor: Oracle Corporation
Specification-Version: 4.2
Specification-Title: JDBC
=====================================
src/test/java/org/postgresql/test/core/ChannelBindingRequiredTest.java
=====================================
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2025, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.core;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+import org.postgresql.PGProperty;
+import org.postgresql.core.ServerVersion;
+import org.postgresql.test.TestUtil;
+import org.postgresql.util.PSQLException;
+import org.postgresql.util.PSQLState;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Locale;
+import java.util.Properties;
+
+class ChannelBindingRequiredTest {
+
+ private static final String MD5_ROLE_NAME = "test_channel_binding_md5";
+ private static final String SASL_ROLE_NAME = "test_channel_binding_sasl";
+ private static final String TEST_PASSWORD = "test_password_123";
+
+ @BeforeAll
+ static void setUp() throws Exception {
+ TestUtil.assumeHaveMinimumServerVersion(ServerVersion.v11);
+
+ try (Connection conn = TestUtil.openPrivilegedDB()) {
+ String sslEnabled = TestUtil.queryForString(conn, "SHOW ssl");
+ assumeTrue("on".equals(sslEnabled), "SSL must be enabled for channel binding tests");
+
+ TestUtil.execute(conn, "SET password_encryption = 'md5'");
+ TestUtil.execute(conn, "DROP ROLE IF EXISTS " + MD5_ROLE_NAME);
+ TestUtil.execute(conn, "CREATE ROLE " + MD5_ROLE_NAME + " WITH LOGIN PASSWORD '" + TEST_PASSWORD + "'");
+
+ TestUtil.execute(conn,"SET password_encryption='scram-sha-256'");
+ TestUtil.execute(conn,"DROP ROLE IF EXISTS " + SASL_ROLE_NAME);
+ TestUtil.execute(conn,"CREATE ROLE " + SASL_ROLE_NAME + " WITH LOGIN PASSWORD '" + TEST_PASSWORD + "'");
+ }
+ }
+
+ @AfterAll
+ static void tearDown() throws Exception {
+ try (Connection conn = TestUtil.openPrivilegedDB()) {
+ TestUtil.execute(conn, "DROP ROLE IF EXISTS " + MD5_ROLE_NAME);
+ TestUtil.execute(conn, "DROP ROLE IF EXISTS " + SASL_ROLE_NAME);
+ }
+ }
+
+ /**
+ * Test that md5 authentication fails when channel binding is required.
+ * Channel binding is only supported with SSL + SCRAM authentication.
+ */
+ @Test
+ void testMD5AuthWithChannelBindingRequiredFails() {
+ Properties props = new Properties();
+ PGProperty.USER.set(props, MD5_ROLE_NAME);
+ PGProperty.PASSWORD.set(props, TEST_PASSWORD);
+ PGProperty.CHANNEL_BINDING.set(props, "require");
+ PGProperty.SSL_MODE.set(props, "require");
+
+ PSQLException ex = assertThrows(PSQLException.class, () -> TestUtil.openDB(props),
+ "Connection with MD5 auth and channel binding required should fail");
+
+ assertEquals(PSQLState.CONNECTION_REJECTED.getState(), ex.getSQLState());
+ String errorMessage = ex.getMessage().toLowerCase(Locale.ROOT);
+ assertTrue(errorMessage.contains("channel binding") && errorMessage.contains("md5"),
+ "Error message should mention both channel binding requirement and MD5 authentication: " + ex.getMessage());
+ }
+
+
+ /**
+ * Test that SCRAM authentication fails when channel binding is required with no SSL.
+ * Channel binding is only supported with SSL + SCRAM authentication.
+ */
+ @Test
+ void testScramAuthWithNoSSLChannelBindingRequiredFails() {
+ Properties props = new Properties();
+ PGProperty.USER.set(props, SASL_ROLE_NAME);
+ PGProperty.PASSWORD.set(props, TEST_PASSWORD);
+ PGProperty.CHANNEL_BINDING.set(props, "require");
+ PGProperty.SSL_MODE.set(props, "disable");
+
+ PSQLException ex = assertThrows(PSQLException.class, () -> TestUtil.openDB(props),
+ "Connection with SCRAM auth and channel binding required should fail without SSL");
+
+ assertEquals(PSQLState.CONNECTION_REJECTED.getState(), ex.getSQLState());
+ String errorMessage = ex.getMessage().toLowerCase(Locale.ROOT);
+ assertTrue(errorMessage.contains("channel binding") && errorMessage.contains("ssl"),
+ "Error message should mention both channel binding requirement and ssl: " + ex.getMessage());
+ }
+
+ /**
+ * Test that SASL authentication succeeds when channel binding is required.
+ * This should work as channel binding is supported with SCRAM authentication.
+ */
+ @Test
+ void testSASLAuthWithChannelBindingRequiredSucceeds() throws SQLException {
+ Properties props = new Properties();
+ PGProperty.USER.set(props, SASL_ROLE_NAME);
+ PGProperty.PASSWORD.set(props, TEST_PASSWORD);
+ PGProperty.CHANNEL_BINDING.set(props, "require");
+ PGProperty.SSL_MODE.set(props, "require");
+
+ try (Connection conn = TestUtil.openDB(props);
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT current_user")) {
+ assertTrue(rs.next(), "Has result row");
+ assertEquals(SASL_ROLE_NAME, rs.getString(1));
+ }
+ }
+}
=====================================
src/test/java/org/postgresql/test/jdbc2/AutoRollbackTest.java
=====================================
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2004, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.jdbc2;
+
+import org.postgresql.PGConnection;
+import org.postgresql.PGProperty;
+import org.postgresql.core.BaseConnection;
+import org.postgresql.core.ResultHandler;
+import org.postgresql.core.ServerVersion;
+import org.postgresql.core.TransactionState;
+import org.postgresql.jdbc.AutoSave;
+import org.postgresql.jdbc.PgConnection;
+import org.postgresql.jdbc.PreferQueryMode;
+import org.postgresql.test.TestUtil;
+import org.postgresql.util.PSQLState;
+
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicInteger;
+
+ at RunWith(Parameterized.class)
+public class AutoRollbackTest extends BaseTest4 {
+ private static final AtomicInteger counter = new AtomicInteger();
+
+ private enum CleanSavePoint {
+ TRUE,
+ FALSE
+ }
+
+ private enum FailMode {
+ /**
+ * Executes "select 1/0" and causes transaction failure (if autocommit=no).
+ * Mitigation: "autosave=always" or "autocommit=true"
+ */
+ SELECT,
+ /**
+ * Executes "alter table rollbacktest", thus it breaks a prepared select over that table.
+ * Mitigation: "autosave in (always, conservative)"
+ */
+ ALTER,
+ /**
+ * Executes DEALLOCATE ALL.
+ * Mitigation:
+ * 1) QueryExecutor tracks "DEALLOCATE ALL" responses ({@see org.postgresql.core.QueryExecutor#setFlushCacheOnDeallocate(boolean)}
+ * 2) QueryExecutor tracks "prepared statement name is invalid" and unprepared relevant statements ({@link org.postgresql.core.v3.QueryExecutorImpl#processResults(ResultHandler, int)}
+ * 3) "autosave in (always, conservative)"
+ * 4) Non-transactional cases are healed by retry (when no transaction present, just retry is possible)
+ */
+ DEALLOCATE,
+ /**
+ * Executes DISCARD ALL.
+ * Mitigation: the same as for {@link #DEALLOCATE}
+ */
+ DISCARD,
+ /**
+ * Executes "insert ... select 1/0" in a batch statement, thus causing the transaction to fail.
+ */
+ INSERT_BATCH,
+ }
+
+ private enum ReturnColumns {
+ EXACT("a, str"),
+ STAR("*");
+
+ public final String cols;
+
+ ReturnColumns(String cols) {
+ this.cols = cols;
+ }
+ }
+
+ private enum TestStatement {
+ SELECT("select ${cols} from rollbacktest", 0),
+ WITH_INSERT_SELECT(
+ "with x as (insert into rollbacktest(a, str) values(43, 'abc') returning ${cols})"
+ + "select * from x", 1);
+
+ private final String sql;
+ private final int rowsInserted;
+
+ TestStatement(String sql, int rowsInserted) {
+ this.sql = sql;
+ this.rowsInserted = rowsInserted;
+ }
+
+ public String getSql(ReturnColumns cols) {
+ return sql.replace("${cols}", cols.cols);
+ }
+ }
+
+ private static final EnumSet<FailMode> DEALLOCATES =
+ EnumSet.of(FailMode.DEALLOCATE, FailMode.DISCARD);
+
+ private static final EnumSet<FailMode> TRANS_KILLERS =
+ EnumSet.of(FailMode.SELECT, FailMode.INSERT_BATCH);
+
+ private enum ContinueMode {
+ COMMIT,
+ IS_VALID,
+ SELECT,
+ }
+
+ private final AutoSave autoSave;
+ private final CleanSavePoint cleanSavePoint;
+ private final AutoCommit autoCommit;
+ private final FailMode failMode;
+ private final ContinueMode continueMode;
+ private final boolean flushCacheOnDeallocate;
+ private final boolean trans;
+ private final TestStatement testSql;
+ private final ReturnColumns cols;
+
+ public AutoRollbackTest(AutoSave autoSave, CleanSavePoint cleanSavePoint, AutoCommit autoCommit,
+ FailMode failMode, ContinueMode continueMode, boolean flushCacheOnDeallocate,
+ boolean trans, TestStatement testSql, ReturnColumns cols) {
+ this.autoSave = autoSave;
+ this.cleanSavePoint = cleanSavePoint;
+ this.autoCommit = autoCommit;
+ this.failMode = failMode;
+ this.continueMode = continueMode;
+ this.flushCacheOnDeallocate = flushCacheOnDeallocate;
+ this.trans = trans;
+ this.testSql = testSql;
+ this.cols = cols;
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ if (testSql == TestStatement.WITH_INSERT_SELECT) {
+ assumeMinimumServerVersion(ServerVersion.v9_1);
+ }
+
+ TestUtil.createTable(con, "rollbacktest", "a int, str text");
+ con.setAutoCommit(autoCommit == AutoCommit.YES);
+ BaseConnection baseConnection = con.unwrap(BaseConnection.class);
+ baseConnection.setFlushCacheOnDeallocate(flushCacheOnDeallocate);
+ Assume.assumeTrue("DEALLOCATE ALL requires PostgreSQL 8.3+",
+ failMode != FailMode.DEALLOCATE || TestUtil.haveMinimumServerVersion(con, ServerVersion.v8_3));
+ Assume.assumeTrue("DISCARD ALL requires PostgreSQL 8.3+",
+ failMode != FailMode.DISCARD || TestUtil.haveMinimumServerVersion(con, ServerVersion.v8_3));
+ Assume.assumeTrue("Plan invalidation on table redefinition requires PostgreSQL 8.3+",
+ failMode != FailMode.ALTER || TestUtil.haveMinimumServerVersion(con, ServerVersion.v8_3));
+ }
+
+ @Override
+ public void tearDown() throws SQLException {
+ try {
+ con.setAutoCommit(true);
+ TestUtil.dropTable(con, "rollbacktest");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ super.tearDown();
+ }
+
+ @Override
+ protected void updateProperties(Properties props) {
+ super.updateProperties(props);
+ PGProperty.AUTOSAVE.set(props, autoSave.value());
+ PGProperty.CLEANUP_SAVEPOINTS.set(props, cleanSavePoint.toString());
+ PGProperty.PREPARE_THRESHOLD.set(props, 1);
+ }
+
+ @Parameterized.Parameters(name = "{index}: autorollback(autoSave={0}, cleanSavePoint={1}, autoCommit={2}, failMode={3}, continueMode={4}, flushOnDeallocate={5}, hastransaction={6}, sql={7}, columns={8})")
+ public static Iterable<Object[]> data() {
+ Collection<Object[]> ids = new ArrayList<>();
+ boolean[] booleans = new boolean[]{true, false};
+ for (AutoSave autoSave : AutoSave.values()) {
+ for (CleanSavePoint cleanSavePoint:CleanSavePoint.values()) {
+ for (AutoCommit autoCommit : AutoCommit.values()) {
+ for (FailMode failMode : FailMode.values()) {
+ // ERROR: DISCARD ALL cannot run inside a transaction block
+ if (failMode == FailMode.DISCARD && autoCommit == AutoCommit.NO) {
+ continue;
+ }
+ for (ContinueMode continueMode : ContinueMode.values()) {
+ if (failMode == FailMode.ALTER && continueMode != ContinueMode.SELECT) {
+ continue;
+ }
+ for (boolean flushCacheOnDeallocate : booleans) {
+ if (!(flushCacheOnDeallocate || DEALLOCATES.contains(failMode))) {
+ continue;
+ }
+
+ for (boolean trans : new boolean[]{true, false}) {
+ // continueMode would commit, and autoCommit=YES would commit,
+ // so it does not make sense to test trans=true for those cases
+ if (trans && (continueMode == ContinueMode.COMMIT
+ || autoCommit != AutoCommit.NO)) {
+ continue;
+ }
+ for (TestStatement statement : TestStatement.values()) {
+ for (ReturnColumns columns : ReturnColumns.values()) {
+ ids.add(new Object[]{autoSave, cleanSavePoint, autoCommit, failMode, continueMode,
+ flushCacheOnDeallocate, trans, statement, columns});
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return ids;
+ }
+
+ @Test
+ public void run() throws SQLException {
+ if (continueMode == ContinueMode.IS_VALID) {
+ // make "isValid" a server-prepared statement
+ con.isValid(4);
+ } else if (continueMode == ContinueMode.COMMIT) {
+ doCommit();
+ } else if (continueMode == ContinueMode.SELECT) {
+ assertRows("rollbacktest", 0);
+ }
+
+ Statement statement = con.createStatement();
+ statement.executeUpdate("insert into rollbacktest(a, str) values (0, 'test')");
+ int rowsExpected = 1;
+
+ PreparedStatement ps = con.prepareStatement(testSql.getSql(cols));
+ // Server-prepare the testSql
+ ps.executeQuery().close();
+ rowsExpected += testSql.rowsInserted;
+
+ if (trans) {
+ statement.executeUpdate("update rollbacktest set a=a");
+ }
+
+ switch (failMode) {
+ case SELECT:
+ try {
+ statement.execute("select 1/0");
+ Assert.fail("select 1/0 should fail");
+ } catch (SQLException e) {
+ Assert.assertEquals("division by zero expected",
+ PSQLState.DIVISION_BY_ZERO.getState(), e.getSQLState());
+ }
+ break;
+ case DEALLOCATE:
+ statement.executeUpdate("DEALLOCATE ALL");
+ break;
+ case DISCARD:
+ statement.executeUpdate("DISCARD ALL");
+ break;
+ case ALTER:
+ statement.executeUpdate("alter table rollbacktest add q int");
+ break;
+ case INSERT_BATCH:
+ try {
+ statement.addBatch("insert into rollbacktest(a, str) values (1/0, 'test')");
+ statement.executeBatch();
+ Assert.fail("select 1/0 should fail");
+ } catch (SQLException e) {
+ Assert.assertEquals("division by zero expected",
+ PSQLState.DIVISION_BY_ZERO.getState(), e.getSQLState());
+ }
+ break;
+ default:
+ Assert.fail("Fail mode " + failMode + " is not implemented");
+ }
+
+ PgConnection pgConnection = con.unwrap(PgConnection.class);
+ if (autoSave == AutoSave.ALWAYS) {
+ Assert.assertNotEquals("In AutoSave.ALWAYS, transaction should not fail",
+ TransactionState.FAILED, pgConnection.getTransactionState());
+ }
+ if (autoCommit == AutoCommit.NO) {
+ Assert.assertNotEquals("AutoCommit == NO, thus transaction should be active (open or failed)",
+ TransactionState.IDLE, pgConnection.getTransactionState());
+ }
+ statement.close();
+
+ switch (continueMode) {
+ case COMMIT:
+ try {
+ doCommit();
+ // No assert here: commit should always succeed with exception of well known failure cases in catch
+ } catch (SQLException e) {
+ if (!flushCacheOnDeallocate && DEALLOCATES.contains(failMode)
+ && autoSave == AutoSave.NEVER) {
+ Assert.assertEquals(
+ "flushCacheOnDeallocate is disabled, thus " + failMode + " should cause 'prepared statement \"...\" does not exist'"
+ + " error message is " + e.getMessage(),
+ PSQLState.INVALID_SQL_STATEMENT_NAME.getState(), e.getSQLState());
+ return;
+ }
+ throw e;
+ }
+ return;
+ case IS_VALID:
+ Assert.assertTrue("Connection.isValid should return true unless the connection is closed",
+ con.isValid(4));
+ return;
+ default:
+ break;
+ }
+
+ try {
+ // Try execute server-prepared statement again
+ ps.executeQuery().close();
+ rowsExpected += testSql.rowsInserted;
+ executeSqlSuccess();
+ } catch (SQLException e) {
+ if (autoSave != AutoSave.ALWAYS && TRANS_KILLERS.contains(failMode) && autoCommit == AutoCommit.NO) {
+ Assert.assertEquals(
+ "AutoSave==" + autoSave + ", thus statements should fail with 'current transaction is aborted...', "
+ + " error message is " + e.getMessage(),
+ PSQLState.IN_FAILED_SQL_TRANSACTION.getState(), e.getSQLState());
+ return;
+ }
+
+ if (autoSave == AutoSave.NEVER && autoCommit == AutoCommit.NO) {
+ if (DEALLOCATES.contains(failMode) && !flushCacheOnDeallocate) {
+ Assert.assertEquals(
+ "flushCacheOnDeallocate is disabled, thus " + failMode + " should cause 'prepared statement \"...\" does not exist'"
+ + " error message is " + e.getMessage(),
+ PSQLState.INVALID_SQL_STATEMENT_NAME.getState(), e.getSQLState());
+ } else if (failMode == FailMode.ALTER) {
+ Assert.assertEquals(
+ "AutoSave==NEVER, autocommit=NO, thus ALTER TABLE causes SELECT * to fail with "
+ + "'cached plan must not change result type', "
+ + " error message is " + e.getMessage(),
+ PSQLState.NOT_IMPLEMENTED.getState(), e.getSQLState());
+ } else {
+ throw e;
+ }
+ } else {
+ throw e;
+ }
+ }
+
+ try {
+ assertRows("rollbacktest", rowsExpected);
+ executeSqlSuccess();
+ } catch (SQLException e) {
+ if (autoSave == AutoSave.NEVER && autoCommit == AutoCommit.NO) {
+ if (DEALLOCATES.contains(failMode) && !flushCacheOnDeallocate
+ || failMode == FailMode.ALTER) {
+ // The above statement failed with "prepared statement does not exist", thus subsequent one should fail with
+ // transaction aborted.
+ Assert.assertEquals(
+ "AutoSave==NEVER, thus statements should fail with 'current transaction is aborted...', "
+ + " error message is " + e.getMessage(),
+ PSQLState.IN_FAILED_SQL_TRANSACTION.getState(), e.getSQLState());
+ }
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ private void executeSqlSuccess() throws SQLException {
+ if (autoCommit == AutoCommit.YES) {
+ // in autocommit everything should just work
+ } else if (TRANS_KILLERS.contains(failMode)) {
+ if (autoSave != AutoSave.ALWAYS) {
+ Assert.fail(
+ "autosave= " + autoSave + " != ALWAYS, thus the transaction should be killed");
+ }
+ } else if (DEALLOCATES.contains(failMode)) {
+ if (autoSave == AutoSave.NEVER && !flushCacheOnDeallocate
+ && con.unwrap(PGConnection.class).getPreferQueryMode() != PreferQueryMode.SIMPLE) {
+ Assert.fail("flushCacheOnDeallocate == false, thus DEALLOCATE ALL should kill the transaction");
+ }
+ } else if (failMode == FailMode.ALTER) {
+ if (autoSave == AutoSave.NEVER
+ && con.unwrap(PGConnection.class).getPreferQueryMode() != PreferQueryMode.SIMPLE
+ && cols == ReturnColumns.STAR) {
+ Assert.fail("autosave=NEVER, thus the transaction should be killed");
+ }
+ } else {
+ Assert.fail("It is not specified why the test should pass, thus marking a failure");
+ }
+ }
+
+ private void assertRows(String tableName, int nrows) throws SQLException {
+ Statement st = con.createStatement();
+ ResultSet rs = st.executeQuery("select count(*) from " + tableName);
+ rs.next();
+ Assert.assertEquals("Table " + tableName, nrows, rs.getInt(1));
+ }
+
+ private void doCommit() throws SQLException {
+ // Such a dance is required since "commit" checks "current transaction state",
+ // so we need some pending changes, so "commit" query would be sent to the database
+ if (con.getAutoCommit()) {
+ con.setAutoCommit(false);
+ Statement st = con.createStatement();
+ st.executeUpdate(
+ "insert into rollbacktest(a, str) values (42, '" + System.currentTimeMillis() + "," + counter.getAndIncrement() + "')");
+ st.close();
+ }
+ con.commit();
+ con.setAutoCommit(autoCommit == AutoCommit.YES);
+ }
+}
View it on GitLab: https://salsa.debian.org/java-team/libpostgresql-jdbc-java/-/commit/4ac37a140a26360eb677b5a0ee3bdf115ae72a51
--
View it on GitLab: https://salsa.debian.org/java-team/libpostgresql-jdbc-java/-/commit/4ac37a140a26360eb677b5a0ee3bdf115ae72a51
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20250613/814c77c5/attachment.htm>
More information about the pkg-java-commits
mailing list