Skip to content

feat: flushCacheOnDdl — transparent re-prepare after CREATE/DROP/ALTER - #4067

Merged
vlsi merged 1 commit into
pgjdbc:masterfrom
vlsi:feat/flush-cache-on-ddl
May 18, 2026
Merged

vlsi merged 1 commit into
pgjdbc:masterfrom
vlsi:feat/flush-cache-on-ddl

Conversation

@vlsi

@vlsi vlsi commented May 13, 2026

Copy link
Copy Markdown
Member

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

  • 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.
  • PGPropertyTest validates getter/setter wiring and alphabetic enum ordering. All green.
  • 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

…P/ALTER

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. Today pgjdbc surfaces that error
verbatim, and transparent recovery requires opting into autosave=ALWAYS
(which wraps every query in a savepoint).

This change adds a new connection property, flushCacheOnDdl (default
true), that makes the driver invalidate the 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. flushCacheOnDdl=false preserves the
pre-existing behaviour for users who rely on it.

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:

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; the assertion in
executeSqlSuccess only flags the legacy hard-failure case when
flushCacheOnDdl is explicitly disabled.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@vlsi vlsi added the bug label May 18, 2026
@vlsi
vlsi merged commit 3fbf771 into pgjdbc:master May 18, 2026
14 checks passed
davecramer added a commit to davecramer/pgjdbc that referenced this pull request Jul 6, 2026
Rename the release notes to 2026-07-06 to reflect the release date, and
add the remaining PR links (flushCacheOnDdl pgjdbc#4067, connectExecutor
pgjdbc#4165, reWriteBatchedInserts cap pgjdbc#4207, OID constants pgjdbc#4220, 64-bit LO
pgjdbc#4204, and the refactor entries pgjdbc#4160/pgjdbc#4166/pgjdbc#4215).
davecramer added a commit that referenced this pull request Jul 6, 2026
* docs: add 42.7.12 release changelog

Covers all changes since REL42.7.11: search_path GUC_REPORT cache
invalidation (PG 18+), flushCacheOnDdl, connectExecutor,
classLoaderStrategy, reWriteBatchedInserts protocol-limit cap,
large object 64-bit API, updatable ResultSet search_path fix,
XA autoCommit fix, FIPS trust anchors, SSL key-format auto-detection,
batching improvements, and numerous other bug fixes.

docs: prepare 42.7.13 release notes after 42.7.12 security release

42.7.12 shipped as a security-only release for the silent channel-binding
downgrade (CVE-2026-54291), so the feature and bug-fix notes that had been
drafted for 42.7.12 now belong to 42.7.13.

- bump pgjdbc.version to 42.7.13
- add a published 42.7.12 changelog entry covering CVE-2026-54291
- retitle the drafted maintenance notes from 42.7.12 to 42.7.13 (CHANGELOG.md
  and the website changelog)
- record PR #4248 (LargeObject.close() flushes before marking closed, and
  lo_close no longer masks a flush failure) under 42.7.13

docs: fix PR links and add missing entries in 42.7.13 release notes

Point each changelog entry at the PR that merged the change rather than
at the issue or at the superseded #3400. Add two entries that were
missing: the reWriteBatchedInserts parameterless-INSERT fix (#4207) and
the GitHub release jar/signature fix (#3814). Drop the stray
connectThreadFactory entry from CHANGELOG.md, which connectExecutor
replaced before release.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

docs: credit translators and attribute large-object commits to James Howe

Move the BlobInputStream skip and reset commits to James Howe, who
co-authored them, and add a short contributors note crediting the
Italian, Simplified Chinese, and Russian-review translators.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

docs: add the empty-timestamp fix (#4278) to the 42.7.13 release notes

Reflect PR #4278, merged after the release-notes branch was opened: an
empty timestamp/timestamptz/date value now yields a clear SQLException
instead of ArrayIndexOutOfBoundsException.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* docs: diarize all 42.7.13 changes in CHANGELOG.md

The root CHANGELOG.md [42.7.13] section listed only 20 entries while
about 50 user-facing changes have landed since the 42.7.12 baseline.
Bring it in line with the reviewed website changelog: add the missing
feat/fix/refactor entries and correct PR references.

Newly diarized items include getSchemas() on a hot standby (#4274),
getColumnPrivileges()/CHAR_OCTET_LENGTH (#4231), FIPS PKIX trust
anchors (#4193), SSL key-format auto-detection (#3946), GSS response
timeout, autosave savepoints for SET LOCAL (#4203), BatchResultHandler
on a closed connection (#4187), SQL_TSI_FRAC_SECOND (#4229), the
batching/describe fixes, NumberParser/PGInterval validation,
connectThreadFactory, OID constants, and getPrimaryKeys derivation.

Docs, i18n, build, ci, test, and dependency-bump commits are omitted,
consistent with the file's existing "Notable changes" scope.

* docs: date the 42.7.13 website changelog to its release date

Rename the release notes to 2026-07-06 to reflect the release date, and
add the remaining PR links (flushCacheOnDdl #4067, connectExecutor
#4165, reWriteBatchedInserts cap #4207, OID constants #4220, 64-bit LO
#4204, and the refactor entries #4160/#4166/#4215).

* docs: correct the 42.7.13 commits-by-author counts

Reconcile the "Commits by author" section with git history:
- Vladimir Sitnikov: 100 -> 104 (stale count carried from the 42.7.12 draft)
- Dave Cramer: 4 -> 5, add the missing "maintainer vs committer" (#4234) entry
- add Michael Baumgartner (1 commit), previously omitted

James Howe remains credited for the two co-authored large-object commits.

* remove connectThread as it was replaced by connectExecutor

---------

Co-authored-by: Vladimir Sitnikov <[email protected]>
vlsi added a commit to vlsi/pgjdbc that referenced this pull request Jul 8, 2026
* docs: add 42.7.12 release changelog

Covers all changes since REL42.7.11: search_path GUC_REPORT cache
invalidation (PG 18+), flushCacheOnDdl, connectExecutor,
classLoaderStrategy, reWriteBatchedInserts protocol-limit cap,
large object 64-bit API, updatable ResultSet search_path fix,
XA autoCommit fix, FIPS trust anchors, SSL key-format auto-detection,
batching improvements, and numerous other bug fixes.

docs: prepare 42.7.13 release notes after 42.7.12 security release

42.7.12 shipped as a security-only release for the silent channel-binding
downgrade (CVE-2026-54291), so the feature and bug-fix notes that had been
drafted for 42.7.12 now belong to 42.7.13.

- bump pgjdbc.version to 42.7.13
- add a published 42.7.12 changelog entry covering CVE-2026-54291
- retitle the drafted maintenance notes from 42.7.12 to 42.7.13 (CHANGELOG.md
  and the website changelog)
- record PR pgjdbc#4248 (LargeObject.close() flushes before marking closed, and
  lo_close no longer masks a flush failure) under 42.7.13

docs: fix PR links and add missing entries in 42.7.13 release notes

Point each changelog entry at the PR that merged the change rather than
at the issue or at the superseded pgjdbc#3400. Add two entries that were
missing: the reWriteBatchedInserts parameterless-INSERT fix (pgjdbc#4207) and
the GitHub release jar/signature fix (pgjdbc#3814). Drop the stray
connectThreadFactory entry from CHANGELOG.md, which connectExecutor
replaced before release.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

docs: credit translators and attribute large-object commits to James Howe

Move the BlobInputStream skip and reset commits to James Howe, who
co-authored them, and add a short contributors note crediting the
Italian, Simplified Chinese, and Russian-review translators.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

docs: add the empty-timestamp fix (pgjdbc#4278) to the 42.7.13 release notes

Reflect PR pgjdbc#4278, merged after the release-notes branch was opened: an
empty timestamp/timestamptz/date value now yields a clear SQLException
instead of ArrayIndexOutOfBoundsException.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* docs: diarize all 42.7.13 changes in CHANGELOG.md

The root CHANGELOG.md [42.7.13] section listed only 20 entries while
about 50 user-facing changes have landed since the 42.7.12 baseline.
Bring it in line with the reviewed website changelog: add the missing
feat/fix/refactor entries and correct PR references.

Newly diarized items include getSchemas() on a hot standby (pgjdbc#4274),
getColumnPrivileges()/CHAR_OCTET_LENGTH (pgjdbc#4231), FIPS PKIX trust
anchors (pgjdbc#4193), SSL key-format auto-detection (pgjdbc#3946), GSS response
timeout, autosave savepoints for SET LOCAL (pgjdbc#4203), BatchResultHandler
on a closed connection (pgjdbc#4187), SQL_TSI_FRAC_SECOND (pgjdbc#4229), the
batching/describe fixes, NumberParser/PGInterval validation,
connectThreadFactory, OID constants, and getPrimaryKeys derivation.

Docs, i18n, build, ci, test, and dependency-bump commits are omitted,
consistent with the file's existing "Notable changes" scope.

* docs: date the 42.7.13 website changelog to its release date

Rename the release notes to 2026-07-06 to reflect the release date, and
add the remaining PR links (flushCacheOnDdl pgjdbc#4067, connectExecutor
pgjdbc#4165, reWriteBatchedInserts cap pgjdbc#4207, OID constants pgjdbc#4220, 64-bit LO
pgjdbc#4204, and the refactor entries pgjdbc#4160/pgjdbc#4166/pgjdbc#4215).

* docs: correct the 42.7.13 commits-by-author counts

Reconcile the "Commits by author" section with git history:
- Vladimir Sitnikov: 100 -> 104 (stale count carried from the 42.7.12 draft)
- Dave Cramer: 4 -> 5, add the missing "maintainer vs committer" (pgjdbc#4234) entry
- add Michael Baumgartner (1 commit), previously omitted

James Howe remains credited for the two co-authored large-object commits.

* remove connectThread as it was replaced by connectExecutor

---------

Co-authored-by: Vladimir Sitnikov <[email protected]>
vlsi added a commit to vlsi/pgjdbc that referenced this pull request Jul 8, 2026
* docs: add 42.7.12 release changelog

Covers all changes since REL42.7.11: search_path GUC_REPORT cache
invalidation (PG 18+), flushCacheOnDdl, connectExecutor,
classLoaderStrategy, reWriteBatchedInserts protocol-limit cap,
large object 64-bit API, updatable ResultSet search_path fix,
XA autoCommit fix, FIPS trust anchors, SSL key-format auto-detection,
batching improvements, and numerous other bug fixes.

docs: prepare 42.7.13 release notes after 42.7.12 security release

42.7.12 shipped as a security-only release for the silent channel-binding
downgrade (CVE-2026-54291), so the feature and bug-fix notes that had been
drafted for 42.7.12 now belong to 42.7.13.

- bump pgjdbc.version to 42.7.13
- add a published 42.7.12 changelog entry covering CVE-2026-54291
- retitle the drafted maintenance notes from 42.7.12 to 42.7.13 (CHANGELOG.md
  and the website changelog)
- record PR pgjdbc#4248 (LargeObject.close() flushes before marking closed, and
  lo_close no longer masks a flush failure) under 42.7.13

docs: fix PR links and add missing entries in 42.7.13 release notes

Point each changelog entry at the PR that merged the change rather than
at the issue or at the superseded pgjdbc#3400. Add two entries that were
missing: the reWriteBatchedInserts parameterless-INSERT fix (pgjdbc#4207) and
the GitHub release jar/signature fix (pgjdbc#3814). Drop the stray
connectThreadFactory entry from CHANGELOG.md, which connectExecutor
replaced before release.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

docs: credit translators and attribute large-object commits to James Howe

Move the BlobInputStream skip and reset commits to James Howe, who
co-authored them, and add a short contributors note crediting the
Italian, Simplified Chinese, and Russian-review translators.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

docs: add the empty-timestamp fix (pgjdbc#4278) to the 42.7.13 release notes

Reflect PR pgjdbc#4278, merged after the release-notes branch was opened: an
empty timestamp/timestamptz/date value now yields a clear SQLException
instead of ArrayIndexOutOfBoundsException.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* docs: diarize all 42.7.13 changes in CHANGELOG.md

The root CHANGELOG.md [42.7.13] section listed only 20 entries while
about 50 user-facing changes have landed since the 42.7.12 baseline.
Bring it in line with the reviewed website changelog: add the missing
feat/fix/refactor entries and correct PR references.

Newly diarized items include getSchemas() on a hot standby (pgjdbc#4274),
getColumnPrivileges()/CHAR_OCTET_LENGTH (pgjdbc#4231), FIPS PKIX trust
anchors (pgjdbc#4193), SSL key-format auto-detection (pgjdbc#3946), GSS response
timeout, autosave savepoints for SET LOCAL (pgjdbc#4203), BatchResultHandler
on a closed connection (pgjdbc#4187), SQL_TSI_FRAC_SECOND (pgjdbc#4229), the
batching/describe fixes, NumberParser/PGInterval validation,
connectThreadFactory, OID constants, and getPrimaryKeys derivation.

Docs, i18n, build, ci, test, and dependency-bump commits are omitted,
consistent with the file's existing "Notable changes" scope.

* docs: date the 42.7.13 website changelog to its release date

Rename the release notes to 2026-07-06 to reflect the release date, and
add the remaining PR links (flushCacheOnDdl pgjdbc#4067, connectExecutor
pgjdbc#4165, reWriteBatchedInserts cap pgjdbc#4207, OID constants pgjdbc#4220, 64-bit LO
pgjdbc#4204, and the refactor entries pgjdbc#4160/pgjdbc#4166/pgjdbc#4215).

* docs: correct the 42.7.13 commits-by-author counts

Reconcile the "Commits by author" section with git history:
- Vladimir Sitnikov: 100 -> 104 (stale count carried from the 42.7.12 draft)
- Dave Cramer: 4 -> 5, add the missing "maintainer vs committer" (pgjdbc#4234) entry
- add Michael Baumgartner (1 commit), previously omitted

James Howe remains credited for the two co-authored large-object commits.

* remove connectThread as it was replaced by connectExecutor

---------

Co-authored-by: Vladimir Sitnikov <[email protected]>
@vlsi vlsi mentioned this pull request Sep 12, 2026
8 of 9 tasks
@rutchkiwi

Copy link
Copy Markdown

I'm upgrading to this version now, and noticed - isn't this kinda wrong?
CREATE TEMP TABLE is emitted quite a lot in my codebase and combined with this, it means the statement cache is basically killed. And I think for no reason? Why would a CREATE TABLE or DROP TABLE of any type need a flush?
Happy to make an separate issue

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants