From fe3cfe446818c6aec35a4824626a72374adf7e01 Mon Sep 17 00:00:00 2001 From: zhangpeng Date: Tue, 21 Nov 2023 16:34:37 +0800 Subject: [PATCH] feat: Support cleanupSavepoints feature --- .../main/java/org/postgresql/PGProperty.java | 10 ++ .../postgresql/core/v3/QueryExecutorImpl.java | 32 +++++- .../postgresql/ds/common/BaseDataSource.java | 24 ++++ .../jdbc2/CleanUpSavePointsPropertyTest.java | 106 ++++++++++++++++++ 4 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 pgjdbc/src/test/java/org/postgresql/test/jdbc2/CleanUpSavePointsPropertyTest.java diff --git a/pgjdbc/src/main/java/org/postgresql/PGProperty.java b/pgjdbc/src/main/java/org/postgresql/PGProperty.java index 3dc150e..19dfa09 100644 --- a/pgjdbc/src/main/java/org/postgresql/PGProperty.java +++ b/pgjdbc/src/main/java/org/postgresql/PGProperty.java @@ -580,6 +580,16 @@ public enum PGProperty { */ OPTIONS("options", null, "Specify 'options' connection initialization parameter."), + /** + * Determine whether SAVEPOINTS used in AUTOSAVE will be released per query or not + */ + CLEANUP_SAVEPOINTS( + "cleanupSavepoints", + "false", + "Determine whether SAVEPOINTS used in AUTOSAVE will be released per query or not", + false, + new String[]{"true", "false"}), + ; private String _name; 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 ad39438..c9b5f93 100644 --- a/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java +++ b/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java @@ -141,6 +141,7 @@ public class QueryExecutorImpl extends QueryExecutorBase { super(pgStream, user, database, cancelSignalTimeout, info); this.allowEncodingChanges = PGProperty.ALLOW_ENCODING_CHANGES.getBoolean(info); + this.cleanupSavePoints = PGProperty.CLEANUP_SAVEPOINTS.getBoolean(info); this.replicationProtocol = new V3ReplicationProtocol(this, pgStream); this.socketAddress = pgStream.getConnectInfo(); readStartupMessages(); @@ -406,6 +407,9 @@ public class QueryExecutorImpl extends QueryExecutorBase { try { handler.handleCompletion(); + if (cleanupSavePoints) { + releaseSavePoint(autosave, flags); + } } catch (SQLException e) { rollbackIfRequired(autosave, e); } @@ -432,6 +436,18 @@ public class QueryExecutorImpl extends QueryExecutorBase { return false; } + private void releaseSavePoint(boolean autosave, int flags) throws SQLException { + if (autosave && getAutoSave() == AutoSave.ALWAYS + && getTransactionState() == TransactionState.OPEN) { + try { + sendOneQuery(releaseAutoSave, SimpleQuery.NO_PARAMETERS, 1, 0, + QUERY_NO_RESULTS | QUERY_NO_METADATA | QUERY_EXECUTE_AS_SIMPLE); + } catch (IOException ex) { + throw new PSQLException(GT.tr("Error releasing savepoint"), PSQLState.IO_ERROR); + } + } + } + private void rollbackIfRequired(boolean autosave, SQLException e) throws SQLException { if (autosave && getTransactionState() == TransactionState.FAILED @@ -558,6 +574,9 @@ public class QueryExecutorImpl extends QueryExecutorBase { try { handler.handleCompletion(); + if (cleanupSavePoints) { + releaseSavePoint(autosave, flags); + } } catch (SQLException e) { rollbackIfRequired(autosave, e); } @@ -622,6 +641,9 @@ public class QueryExecutorImpl extends QueryExecutorBase { try { handler.handleCompletion(); + if (cleanupSavePoints) { + releaseSavePoint(autosave, flags); + } } catch (SQLException e) { rollbackIfRequired(autosave, e); } @@ -2556,8 +2578,8 @@ public class QueryExecutorImpl extends QueryExecutorBase { // For simple 'Q' queries, executeQueue is cleared via ReadyForQuery message } - if (currentQuery == autoSaveQuery) { - // ignore "SAVEPOINT" status from autosave query + if (currentQuery == autoSaveQuery || currentQuery == releaseAutoSave) { + // ignore "SAVEPOINT" or RELEASE SAVEPOINT status from autosave query break; } @@ -3237,6 +3259,7 @@ public class QueryExecutorImpl extends QueryExecutorBase { private long nextUniqueID = 1; private final boolean allowEncodingChanges; + private final boolean cleanupSavePoints; private String output = ""; @@ -3266,6 +3289,11 @@ public class QueryExecutorImpl extends QueryExecutorBase { new NativeQuery("SAVEPOINT PGJDBC_AUTOSAVE", new int[0], false, SqlCommand.BLANK), null, false); + private final SimpleQuery releaseAutoSave = + new SimpleQuery( + new NativeQuery("RELEASE SAVEPOINT PGJDBC_AUTOSAVE", new int[0], false, SqlCommand.BLANK), + null, false); + private final SimpleQuery restoreToAutoSave = new SimpleQuery( new NativeQuery("ROLLBACK TO SAVEPOINT PGJDBC_AUTOSAVE", new int[0], false, SqlCommand.BLANK), diff --git a/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java index 6314828..d671c0d 100644 --- a/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java +++ b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java @@ -1363,6 +1363,22 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable PGProperty.AUTOSAVE.set(properties, autoSave.value()); } + /** + * see PGProperty#CLEANUP_SAVEPOINTS + * @return boolean indicating property set + */ + public boolean getCleanupSavepoints() { + return PGProperty.CLEANUP_SAVEPOINTS.getBoolean(properties); + } + + /** + * see PGProperty#CLEANUP_SAVEPOINTS + * @param cleanupSavepoints will cleanup savepoints after a successful transaction + */ + public void setCleanupSavepoints(boolean cleanupSavepoints) { + PGProperty.CLEANUP_SAVEPOINTS.set(properties, cleanupSavepoints); + } + /** * @see PGProperty#REWRITE_BATCHED_INSERTS * @return boolean indicating property is enabled or not. @@ -1379,6 +1395,14 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable PGProperty.REWRITE_BATCHED_INSERTS.set(properties, reWrite); } + public boolean isCleanupSavePoints() { + return getCleanupSavepoints(); + } + + public void setCleanupSavePoints(final boolean cleanupSavepoints) { + setCleanupSavepoints(cleanupSavepoints); + } + public java.util.logging.Logger getParentLogger() { if(org.postgresql.log.Logger.isUsingJDKLogger()){ return java.util.logging.Logger.getLogger("org.postgresql"); diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/CleanUpSavePointsPropertyTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/CleanUpSavePointsPropertyTest.java new file mode 100644 index 0000000..12e361c --- /dev/null +++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/CleanUpSavePointsPropertyTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023, openGauss Global Development Group + * See the LICENSE file in the project root for more information. + */ +package org.postgresql.test.jdbc2; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.postgresql.test.TestUtil; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; +import java.util.stream.IntStream; + +import static org.junit.Assert.assertEquals; + +/** + * @Projecet pgjdbc + * @Package org.postgresql.jdbc + * @Class CleanUpSavePointsPropertyTest + * @Description: Test case for cleanupSavepoints property. + */ +public class CleanUpSavePointsPropertyTest extends BaseTest4 { + @Before + public void setUp() throws Exception { + Properties props = new Properties(); + props.put("autosave", "always"); + props.put("cleanupSavepoints", "true"); + con = TestUtil.openDB(props); + TestUtil.createTable(con, "savepoint_table", "id int primary key, name varchar(16)"); + con.setAutoCommit(false); + } + + @After + public void tearDown() throws SQLException { + con.setAutoCommit(true); + TestUtil.dropTable(con, "savepoint_table"); + super.tearDown(); + } + + @Test + public void test() throws SQLException { + // add record + IntStream.range(1, 6).forEach(i -> { + try { + addRecord(i, "zp" + i); + } catch (SQLException ex) { + ex.printStackTrace(); + } + }); + // update record + IntStream.range(1, 3).forEach(i -> { + try { + updateRecord(i, "name" + i + "-" + i); + } catch (SQLException e) { + e.printStackTrace(); + } + }); + // delete record + IntStream.range(4, 6).forEach(i -> { + try { + deleteRecord(i); + } catch (SQLException e) { + e.printStackTrace(); + } + }); + // count record + assertEquals(3, countRecord()); + } + + private void addRecord(int id, String name) throws SQLException { + PreparedStatement pstmt = con.prepareStatement("INSERT INTO savepoint_table(id, name) VALUES (?, ?)"); + pstmt.setInt(1, id); + pstmt.setString(2, name); + pstmt.executeUpdate(); + pstmt.close(); + } + + private void updateRecord(int id, String name) throws SQLException { + PreparedStatement pstmt = con.prepareStatement("update savepoint_table set name= ? where id= ?"); + pstmt.setString(1, name); + pstmt.setInt(2, id); + pstmt.executeUpdate(); + pstmt.close(); + } + + private void deleteRecord(int id) throws SQLException { + PreparedStatement pstmt = con.prepareStatement("delete from savepoint_table where id= ?"); + pstmt.setInt(1, id); + pstmt.executeUpdate(); + pstmt.close(); + } + + private int countRecord() throws SQLException { + Statement stmt = con.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM savepoint_table"); + rs.next(); + int count = rs.getInt(1); + rs.close(); + return count; + } +} -- Gitee