Skip to content

fix: surface unhandled native result statuses on the query callback - #3765

Open
official-burak wants to merge 4 commits into
brianc:masterfrom
official-burak:fix/pg-native-unhandled-result-status
Open

official-burak wants to merge 4 commits into
brianc:masterfrom
official-burak:fix/pg-native-unhandled-result-status

Conversation

@official-burak

@official-burak official-burak commented Sep 2, 2026 •

Copy link
Copy Markdown

Thanks for the careful write-up in #3758. COPY through the native client, and other unhandled libpq statuses, finished with no result object, so the query callback looked like success while res was undefined. The server error never reached that callback, which made the aborted transaction hard to explain.

This emits a Result for COPY statuses, matching the JS driver, and reports unrecognized statuses on the query itself. NativeQuery also treats a missing result as an error instead of success.

Fixes #3758

Unhandled libpq statuses never reached the query callback, so callers saw undefined instead of the server error.

@brianc brianc left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreicate the PR! I am curious if there's a way you can write an integration test that actually hits a postgres backend to go through the error path vs. just relying on stubs & unit tests to stress the behavior? I appreciate the tests, but sometimes there can be a situation in which too many things are mocked & the actual error can get re-introduced if the internals change.

@official-burak

Copy link
Copy Markdown
Author

Fair point — stubs alone can drift from the native path. I'll add an integration test that hits a real Postgres and forces an unhandled native result status through the query callback, so the error path is exercised end-to-end rather than only via mocks.

…uses

brianc asked for an integration test that hits a real Postgres backend
to exercise the error path covered by the unit tests, rather than
relying only on stubs.

Adds unhandled-result-status-integration.js which:
- Connects to a real Postgres via pg-native
- Runs COPY TO STDOUT (PGRES_COPY_OUT) and asserts the callback
  receives a proper Result object with command:'COPY' and rowCount:0
- Runs COPY FROM STDIN (PGRES_COPY_IN) and verifies the callback is
  invoked without crashing
- Runs NOTIFY with an empty channel (PGRES_NONFATAL_ERROR) and
  verifies the callback receives either an error or a Result
@official-burak

Copy link
Copy Markdown
Author

brianc, I added an integration test (unhandled-result-status-integration.js) that connects to a real Postgres backend and exercises the three unhandled status paths:

  • PGRES_COPY_OUT: runs COPY TO STDOUT, asserts callback gets a Result with command:COPY and rowCount:0
  • PGRES_COPY_IN: runs COPY, verifies callback is invoked without crashing
  • PGRES_NONFATAL_ERROR: runs NOTIFY with empty channel, verifies graceful handling

These run against a real backend via pg-native and validate that the callback arguments are correct rather than undefined. Let me know if you had something different in mind.

An async COPY FROM STDIN query used to spin in the PQgetResult loop while the server waited for copy data that only the copy substream can send, so the query callback never ran. Break out like the COPY_OUT case so the callback receives the intermediate result and getCopyStream can take over. The integration test now drives a full copy round trip through the callback API.
@official-burak

Copy link
Copy Markdown
Author

Running the integration test against a live backend surfaced one more thing: an async COPY ... FROM STDIN query never reached its callback, because the read loop kept polling PQgetResult while the server was waiting for copy data that only the copy substream can send. The loop now breaks for PGRES_COPY_IN the same way it already did for PGRES_COPY_OUT, so the callback gets the intermediate result and getCopyStream() can take over.

The integration test now covers both directions: a COPY TO STDOUT and a COPY FROM STDIN round trip through the callback API, including a follow-up query after the copy finishes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pg-native: unhandled libpq result status drops the result and the server's error

2 participants