feat: basic Postgres foreign tables support - Foreign Data Wrapper #12127
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
What?
This PR introduces basic support for PostgreSQL foreign tables (via FDW) in Payload CMS by adding an adapter flag to disable
ON CONFLICT DO UPDATE
operations and conditionally skipping foreign key constraints on collections where document locking is disabled.Why?
PostgreSQL’s Foreign Data Wrapper (FDW) allows tables from external databases to be accessed and queried as if they were local. This is very useful when integrating external data sources, think of accounting systems, directly into the Payload schema.
However, FDW tables do not support
ON CONFLICT DO UPDATE
statements and disallow foreign key constraints (used when locking documents), causing Payload to fail during schema setup or on updates (upserts).Supporting foreign tables in Payload:
For background, see:
id
in the Payload-facing database - which can itself be a foreign table.How?
disableOnConflictDoUpdate
option to the PostgreSQL adapter (and mirrored it in SQLite and Drizzle adapters for type safety).lockDocuments
config is set tofalse
, avoiding conflicts with FDW restrictions.Final note: In my opinion, a clearer approach would be to extract upsert logic into a separate
adapter.upsert(...)
method, rather than handling upserts withinadapter.insert(...)
. As it stands, it’s not immediately clear, especially to newcomers, that updates are implemented via insert operations usingonConflictDoUpdate
(which are actually upserts). Refactoring this would require relocating all upsert-related logic (e.g.onConflictDoUpdate
calls) into a dedicatedupsert
function and implementing it across all Drizzle adapters. Perhaps something to think about in the future.