Subject: [pgjdbc/pgjdbc] issue #3889: JDBC Not Sending CLOSE packet

From: "manav-yb (@manav-yb)" <[email-masked]>
Date: Sun, 07 Dec 2025 14:18:09 +0000

Please read https://stackoverflow.com/help/minimal-reproducible-example 

**Describe the issue**
Wrote an JDBC application which uses extended protocol level prepared statements. And using pstmt.close() to deallocate the same prep stmt. But in my server logs I don't see CLOSE packet been sent by driver. 

**Driver Version?** 
postgresql-42.7.1.jar
**Java Version?**
javac 1.8.0_29
**OS Version?**

**PostgreSQL Version?**
PG-15.

**To Reproduce**
Create a table in any database/user. 
CREATE TABLE T_(A INT, B INT); 
Run the below application.

**Expected behaviour**
Expectation is i should get CLOSE packet and prepared statement (`S_1 insert into t_ values ($1, $2)`) should not comes in pg_prepared_statements table after calling pstmt1.close.
What i see is that: prepared statement (S_1 insert into t_ values ($1, $2)) comes even after calling pstmt1.close().

**Logs**
If possible PostgreSQL logs surrounding the occurrence of the issue
Additionally logs from the driver can be obtained adding

Using the following template code make sure the bug can be replicated in the driver alone.
```
import java.sql.*;

public class sendClose3 {

    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement pstmt1 = null, pstmt2 = null;

        try {
            // prepareThreshold=1 is crucial here
            connection = DriverManager.getConnection("jdbc:postgresql://10.150.3.175:5433/yugabyte?prepareThreshold=1", "yugabyte","yugabyte");

            // ---------------------------------------------------------
            // CHANGE 1: Use placeholders (?)
            // This forces the driver to use Extended Query Protocol (Parse/Bind)
            // and create a named statement (e.g., "S_1") on the server.
            // ---------------------------------------------------------
            pstmt1 = connection.prepareStatement("insert into t_ values (?, ?)");
            pstmt1.setInt(1, 1);
            pstmt1.setInt(2, 2);
            
            // Execute twice to ensure the driver switches to server-prepare mode
            pstmt1.execute(); 
            pstmt1.execute();
            pstmt1.execute();
            pstmt1.execute();
            pstmt1.execute();
            pstmt1.execute();
            pstmt1.execute();
            pstmt1.execute();
            pstmt1.execute();
            pstmt1.execute();
            
            // ---------------------------------------------------------
            // CHANGE 2: Close the statement
            // The driver now marks "S_1" as closed internally and queues the packet.
            // ---------------------------------------------------------
            Statement stmt = connection.createStatement();
            System.out.println("Before closing prepared statement");
            ResultSet rs = stmt.executeQuery("select name, statement from pg_prepared_statements");
            while (rs.next()) {
                System.out.println(rs.getString("name") + " " + rs.getString("statement"));
            }
            rs.close();
            // stmt.close();
            pstmt1.close();

            System.out.println("After closing prepared statement");
            rs = stmt.executeQuery("select name, statement from pg_prepared_statements");
            while (rs.next()) {
                System.out.println(rs.getString("name") + " " + rs.getString("statement"));
            }
            rs.close();
            stmt.close();

            connection.close();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

---

From: "vlsi (@vlsi)" <[email-masked]>
Date: Sun, 07 Dec 2025 14:37:16 +0000

pgjdbc has a cache of statements, so it is able to reuse sever-side statements even in the case of `stmt.close`.

See https://stackoverflow.com/questions/32297503/whats-the-life-span-of-a-postgresql-server-side-prepared-statement/32300222#32300222

Why do you expect `CLOSE` message?

---

From: "manav-yb (@manav-yb)" <[email-masked]>
Date: Sun, 07 Dec 2025 15:16:56 +0000

Expecting CLOSE to validate pstmt.close() is sent. 
But from server point of view, not sending actual CLOSE message, I see 2 problems and solutions as well, please correct me if I'm wrong.
1) There can be a resource leak if lot of prepared statements unused been prepared on a long lived backend. But JDBC is configuring it with help of preparedStatementCacheQueries, developer can set it to 0 but he/she won't get advantage of shared prepared statements.
2) On schema change it can cause an Error - "Cached plan must not change result type". Because CLOSE has not  been explicitly send by driver even application has written pstmt.close(). But given JDBC has a the mechansim to retry (when it receives such error) it silently by sending actual CLOSE message followed by new PARSE packet. 

Are there are any other known problems which has or has not workaround.

---

From: "vlsi (@vlsi)" <[email-masked]>
Date: Sun, 07 Dec 2025 15:23:26 +0000

> There can be a resource leak if lot of prepared statements unused been prepared on a long lived backend

The statement cache has limited size per connection, so it is not a memory leak.

> On schema change it can cause an Error - "Cached plan must not change result type".

That is out of control for the jdbc driver. Please ask PostgreSQL database developers to fix the issue.