Skip to content

test: integration tests for client API against real PostgreSQL - #458

Merged
sunng87 merged 3 commits into
masterfrom
test/client-api-integration
Aug 22, 2026
Merged

test: integration tests for client API against real PostgreSQL#458
sunng87 merged 3 commits into
masterfrom
test/client-api-integration

Conversation

@sunng87

@sunng87 sunng87 commented Aug 17, 2026

Copy link
Copy Markdown
Owner

this patch adds integration tests for client-api, and resolves issues found with those tests.

Add tests-integration/client-api, a workspace member that verifies the
pgwire client API (PgWireClient, DefaultStartupHandler, simple and
extended query handlers, cancellation) against a real PostgreSQL 18
server. run.sh manages the server lifecycle with podman; CI gets a
postgres:18 service container job.

The suite exposed and fixes several client-side protocol bugs:

- ExtendedQueryClient::prepare never sent a Describe message, so
  ParameterDescription was never returned and param_types was always
  empty. Prepare now sends Parse + Describe + Sync.
- The extended query methods terminated every step with Sync, which
  ends the extended-query cycle and destroys bound portals (portals do
  not survive the implicit transaction end). Bind now uses Flush to
  keep the cycle open; Execute closes it with Sync only on
  CommandComplete and leaves it open on PortalSuspended; Describe uses
  Flush for portals and Sync for statements; the one-shot query runs
  Parse+Bind+Execute+Sync in a single cycle.
- Error paths left the trailing ReadyForQuery unread (desynchronizing
  the next query) or sent a redundant Sync (making the server emit an
  extra ReadyForQuery with the same effect). Errors now drain to
  ReadyForQuery, syncing only when the cycle is still open; the same
  recovery is applied to PgWireClient::simple_query.
- SimpleQueryHandler::on_message rejected ParameterStatus messages
  that servers emit mid-query (e.g. after SET) as unexpected.
- Tag::from_str failed on multi-word command tags (CREATE TABLE,
  DROP TABLE, ...) and read the INSERT tag's oid/rows in the wrong
  order, disagreeing with Tag-to-CommandComplete encoding.
Servers report parameter changes (e.g. after `SET`) with ParameterStatus
messages both during startup and during query execution. The client only
cached them during startup, leaving server_parameters() stale for the
rest of the session.

Add ClientInfo::set_server_parameter (implemented by PgWireClient to
update its cache) and route every mid-query ParameterStatus through it:

- SimpleQueryHandler::on_message now forwards ParameterStatus to a new
  on_parameter_status hook, whose default implementation updates the
  client cache; custom handlers can override it.
- The extended query client updates the cache in all of its message
  loops (prepare/bind/execute/describe/close/query) and in the
  Sync/finish and error-drain helpers.
- PgWireClient::simple_query's post-error drain updates the cache too.

Verified by integration tests against a real PostgreSQL 18 server: SET
via simple query and via extended query now reflect the new value in
client.server_parameters().
Previously startup ParameterStatus messages were only stored when the
startup handler collected them itself: DefaultStartupHandler kept a
duplicate map that was cloned into ServerInformation at ReadyForQuery
and assigned wholesale to the client, and custom handlers that did not
track parameters ended up with an empty cache (the assignment could
even discard cached entries).

Store parameters on the client as they arrive, mirroring the mid-query
behavior:

- StartupHandler::on_parameter_status gains a default implementation
  that calls ClientInfo::set_server_parameter, so every handler caches
  parameters without extra work.
- DefaultStartupHandler drops its duplicate parameter map;
  on_ready_for_query reports the client's cache instead.
- PgWireClient::connect merges the handler-returned ServerInformation
  over the incrementally cached parameters instead of overwriting, so
  the cache is complete regardless of how the handler builds its
  return value.

The startup integration test now asserts the full GUC_REPORT set
(server_version, server_encoding, client_encoding, application_name,
standard_conforming_strings, integer_datetimes, DateStyle, TimeZone) is
cached, including the application_name echoed back from the Startup
message.
@sunng87
sunng87 merged commit 8f2ccf1 into master Aug 22, 2026
11 checks passed
@sunng87
sunng87 deleted the test/client-api-integration branch August 22, 2026 06:53
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.

1 participant