Skip to content

Expose PG-compatible status/transaction_status/async_exec on the JDBC connection (+ CI Postgres 14)#1220

Open
skunkworker wants to merge 2 commits into
jruby:72-stablefrom
skunkworker:fix/pg-rails-jdbc-methods
Open

Expose PG-compatible status/transaction_status/async_exec on the JDBC connection (+ CI Postgres 14)#1220
skunkworker wants to merge 2 commits into
jruby:72-stablefrom
skunkworker:fix/pg-rails-jdbc-methods

Conversation

@skunkworker

@skunkworker skunkworker commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Under JRuby the PostgreSQL raw connection is a PostgreSQLJdbcConnection, not a PG::Connection. ActiveRecord 7.2's PostgreSQL reconnection test suite calls PG::Connection methods and ::PG::* constants directly on that raw connection, so they blew up with NoMethodError / uninitialized constant PG and aborted those tests.

This PR makes the JDBC connection answer the small libpq-compatible API those paths invoke, and bumps the CI Postgres image to a supported release.

Changes

  • status, transaction_status, async_exec on PostgreSQLRubyJdbcConnection (Java).
  • ::PG::PQTRANS_* / ::PG::CONNECTION_* constants (lib/arjdbc/postgresql/pg_compat.rb), defined only when the real pg gem is absent.
  • CI: PostgreSQL 1114 (separate commit) — PG 11 is EOL and lacks pg_current_xact_id() (PG 13+), which the AR 7.2 suite uses.

Why the PG module shim is necessary

The real pg gem is a C extension and is unavailable under JRuby — the entire reason AR-JDBC exists — so the top-level ::PG namespace and its libpq constants are simply not defined.

AR-JDBC's own production code never references ::PG. Verified by runtime method resolution:

ActiveRecord::Base.connection.method(:retryable_query_error?).owner
#=> ActiveRecord::ConnectionAdapters::AbstractAdapter   # abstract_adapter.rb:1079

It is AR's native PostgreSQL adapter that compares transaction_status to ::PG::PQTRANS_INERROR, but AR-JDBC is class PostgreSQLAdapter < AbstractAdapter (it does not subclass AR's native PG adapter), so that method is never in the ancestry. The other native ::PG sites are bypassed the same way:

So the shim's only consumer is the Rails test suite. Rails' remote_disconnect helper hard-codes ::PG::* on the raw connection, in files this gem cannot edit:

Because those constants are resolved on the caller's side (== ::PG::PQTRANS_INTRANS), nothing our methods return can satisfy them — a top-level ::PG must exist or the tests raise uninitialized constant PG.

Why shim rather than exclude these tests? They exercise behavior AR-JDBC genuinely supports and cares about — reconnect/retry after a dropped backend, the same scenario as the connection-loss work in the sibling PRs. The ::PG references are only test scaffolding used to induce the disconnect, not the behavior under test. With the shim in place these tests run and drop from 26 errors → 3 errors / 4 failures; the remaining 7 are real reconnect-recovery and transaction-state gaps. Excluding the tests would throw away that coverage and mask those genuine gaps. Exclusions are for things that cannot work under JRuby; these can. Hence the shim — an ~8-line, version-stable libpq constants module guarded by unless defined?(PG) — is the correct minimal solution.

Why the constant values / state mapping are correct

  • libpq ConnStatusType (CONNECTION_OK = 0, CONNECTION_BAD = 1) and PGTransactionStatusType (PQTRANS_IDLE = 0, ACTIVE = 1, INTRANS = 2, INERROR = 3, UNKNOWN = 4) — PostgreSQL source:
    libpq-fe.h#L56-L59 and libpq-fe.h#L114-L120
  • transaction_status reads the driver's protocol-level state via BaseConnection#getTransactionState() and maps IDLE/OPEN/FAILEDPQTRANS_IDLE/INTRANS/INERROR. Driver enum:
    pgjdbc TransactionState.java#L8-L11
    (PGConnection exposes no transaction state, hence the unwrap to BaseConnection.)

Verification

Local run of test/cases/adapter_test.rb (jruby-9.4.14.0, AR 7-2-stable, --seed=12345):

runs failures errors
before 67 1 26 (33× transaction_status, 13× status NoMethodError)
after 67 4 3

The remaining 3 errors / 4 failures are deeper reconnect-recovery and transaction-state-tracking gaps, tracked separately. This PR does not target the too many clients connection-leak cascade — that is a distinct issue.

🤖 Generated with Claude Code

skunkworker and others added 2 commits June 25, 2026 13:30
…nection

ActiveRecord's native PostgreSQL reconnect/retry machinery (e.g.
#retryable_query_error? checks transaction_status against PQTRANS_INERROR) and
the Rails test-suite helper #remote_disconnect call PG::Connection methods on
the raw connection, which under JRuby is a PostgreSQLJdbcConnection - not a
PG::Connection. They raised NoMethodError / 'uninitialized constant PG'.

Implement #status, #transaction_status (read from the driver's protocol-level
BaseConnection#getTransactionState) and #async_exec on the JDBC connection, and
define the libpq ::PG::PQTRANS_* / ::PG::CONNECTION_* constants AR references
(only when the real pg gem is absent).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PostgreSQL 11 is EOL and lacks pg_current_xact_id() (added in PG 13), which AR
7.2's test suite uses - causing a cascade of 'function does not exist' /
'current transaction is aborted' errors in the Postgres jobs. Move to a
supported release.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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