Message-ID: From: "anuragg-saxenaa (@anuragg-saxenaa)" To: "pgjdbc/pgjdbc" Date: Mon, 13 Apr 2026 14:39:00 +0000 Subject: [pgjdbc/pgjdbc] PR #4027: fix: drain CopyOut messages after cancel to restore connection usability List-Id: X-GitHub-Author-Id: 17893081 X-GitHub-Author-Login: anuragg-saxenaa X-GitHub-Issue: 4027 X-GitHub-Repo: pgjdbc/pgjdbc X-GitHub-State: open X-GitHub-Type: pull_request X-GitHub-Url: https://github.com/pgjdbc/pgjdbc/pull/4027 Content-Type: text/plain; charset=utf-8 ## Problem When `PGCopyInputStream` is closed before EOF, `close()` calls `cancelCopy()` on the underlying `CopyOut` operation. The `CopyIn` branch in `QueryExecutorImpl.cancelCopy()` correctly sends `CopyFail` and then **drains remaining server messages** (`CopyData`, `CommandComplete`, `ReadyForQuery`) in a loop until the connection lock is released. The `CopyOut` branch only called `sendQueryCancel()` — an out-of-band cancel signal sent on a **separate socket** — but never drained the remaining `CopyData` and `ReadyForQuery` messages from the **main connection buffer**. This left stale server messages on the wire that corrupted subsequent queries, making the connection unusable. Reproducer (from the issue): ```java try (PGCopyInputStream in = new PGCopyInputStream(conn, COPY_SQL)) { byte[] partial = new byte[5]; in.read(partial); // read less than full output // close() → cancelCopy() → stale messages left on wire } // Next query or COPY fails because connection is in broken state ``` ## Fix After `sendQueryCancel()`, add the same `processCopyResults` drain loop used by the `CopyIn` path. This ensures all remaining `CopyData` messages and the final `ReadyForQuery` are consumed, fully resetting the connection before the lock is released. ## Test Added `connectionIsUsableAfterPartialRead()` in `PGCopyInputStreamTest`: performs a partial read, closes the stream, then opens a second `PGCopyInputStream` on the same connection and verifies all rows are returned correctly. Fixes #1290