Message-ID: From: "vlsi (@vlsi)" To: "pgjdbc/pgjdbc" Date: Wed, 13 May 2026 13:17:25 +0000 Subject: [pgjdbc/pgjdbc] PR #4067: feat: flushCacheOnDdl — transparent re-prepare after CREATE/DROP/ALTER List-Id: X-GitHub-Author-Id: 213894 X-GitHub-Author-Login: vlsi X-GitHub-Issue: 4067 X-GitHub-Labels: bug X-GitHub-Repo: pgjdbc/pgjdbc X-GitHub-State: merged X-GitHub-Type: pull_request X-GitHub-Url: https://github.com/pgjdbc/pgjdbc/pull/4067 Content-Type: text/plain; charset=utf-8 ## Summary When a server-prepared statement references a table that is later modified via `ALTER TABLE` (or affected by `CREATE`/`DROP` of a referenced object), PostgreSQL raises `cached plan must not change result type` (SQLSTATE `0A000`) on the next `EXECUTE`. pgjdbc surfaces that error verbatim today, and transparent recovery requires opting into `autosave=ALWAYS`. This PR adds a new connection property, **`flushCacheOnDdl` (default `true`)**, that makes the driver invalidate its prepared-statement cache when it sees a `CREATE`/`DROP`/`ALTER` `CommandComplete` in the same session. The next time the affected statement is executed, the driver re-PARSEs it server-side, so callers never see the cached-plan error. The bump piggybacks on the existing `deallocateEpoch` mechanism used for `DEALLOCATE ALL` / `DISCARD ALL`. Setting `flushCacheOnDdl=false` preserves the pre-existing behaviour for users who rely on it. ### Why default to `true`? The only observable user-facing change is that an opaque `0A000` after `ALTER` no longer reaches the caller. The alternative — `autosave=ALWAYS` — is more expensive (a `SAVEPOINT`/`RELEASE` around every statement) and not the default. For installations that depend on the legacy contract, the new property gives a clean opt-out. ## Wiring - New `PGProperty.FLUSH_CACHE_ON_DDL` and matching `BaseDataSource get/setFlushCacheOnDdl` (`PGPropertyTest` enforces both). - `QueryExecutor.{is,set}FlushCacheOnDdl` + `QueryExecutorBase` implementation, defaulting to `true` so non-`PgConnection` callers (e.g. `ReplicationProtocol`) inherit the improved behaviour. - `PgConnection` reads the property in its constructor and forwards it. - `QueryExecutorImpl` bumps `deallocateEpoch` on `CREATE`/`DROP`/`ALTER` `CommandComplete` when `flushCacheOnDdl` is enabled. ## Test plan - [x] `AutoRollbackTest` now ties its existing `flushCacheOnDeallocate` parameterization to `flushCacheOnDdl` 1:1, and the `FailMode.ALTER` iteration no longer skips `flushCacheOnDeallocate=false`. Both legacy (`cached plan must not change result type` → `IN_FAILED_SQL_TRANSACTION`) and new (transparent re-prepare) paths get coverage. **1056 parameterizations, 0 failures**. - [x] `PGPropertyTest` validates getter/setter wiring and alphabetic enum ordering. **All green**. - [x] Full test suite runs clean locally (only `ConnectTimeoutTest#timeout` flakes on my network — environmental, unrelated to this PR). ## Backwards compatibility - Default behaviour changes: callers that previously observed `0A000` after `ALTER` no longer do. To opt out, set `flushCacheOnDdl=false` in the JDBC URL or `Properties`. - API additions only — `QueryExecutor` gains two methods (`isFlushCacheOnDdl` / `setFlushCacheOnDdl`), and `BaseDataSource` gains the corresponding bean accessors. No method signatures are changed or removed. 🤖 Generated with [Claude Code](https://claude.com/claude-code)