Skip to content

Conversation

@JohnCari
Copy link
Contributor

Summary

Fix PostgreSQL server crash when using prepared statements with foreign tables.

Fixes #482
Related to #237

Problem

PostgreSQL caches query plans after ~5-6 executions (generic plan optimization).
The previous implementation stored a raw pointer to FdwModifyState in
fdw_private, which became invalid after end_foreign_modify freed the state.
Subsequent executions with cached plans dereferenced this stale pointer, causing
a crash.

Solution

  • Add FdwModifyPrivate struct holding serializable reconstruction data
  • Serialize table OID, rowid info (not pointer) in plan_foreign_modify
  • Create fresh FdwModifyState in begin_foreign_modify for each execution

Changes

  • supabase-wrappers/src/modify.rs: Core fix - serialize data instead of pointer
  • wrappers/src/fdw/clickhouse_fdw/tests.rs: Added stress test

Test Plan

  • Added prepared statement stress test (15 INSERTs via parameterized query)
  • Build verified with pg16
  • Clippy clean, rustfmt clean
  • CI validates all PostgreSQL versions (pg13-pg18)

Fix PostgreSQL server crash when using prepared statements with foreign
tables. PostgreSQL caches query plans after ~5-6 executions. The previous
implementation stored a raw pointer to FdwModifyState in fdw_private,
which became invalid after end_foreign_modify freed the state.

Solution:
- Add FdwModifyPrivate struct holding serializable reconstruction data
- Serialize table OID, rowid info (not pointer) in plan_foreign_modify
- Create fresh FdwModifyState in begin_foreign_modify for each execution

Also includes stress test for issue supabase#482 that executes 15 parameterized
INSERT operations to validate the fix.

Fixes supabase#482
Related to supabase#237
Fixes PostgreSQL server crash when using prepared statements with
foreign tables (issue supabase#482).

The root cause was that PostgreSQL caches query plans after ~5-6
executions (generic plan optimization). The previous implementation
stored a raw pointer to FdwModifyState in fdw_private, which became
invalid after end_foreign_modify freed the state. Subsequent executions
with cached plans dereferenced this stale pointer, causing a crash.

Solution:
- Add FdwModifyPrivate struct holding serializable reconstruction data
- Serialize table OID, rowid info (not pointer) in plan_foreign_modify
- Create fresh FdwModifyState in begin_foreign_modify for each execution

This ensures each query execution gets a valid FDW instance and state,
even when PostgreSQL reuses a cached query plan and skips planning.
Validates fix for issue supabase#482 by executing 15 parameterized INSERT
operations - enough to trigger PostgreSQL's generic plan caching.
Before the fix, this would crash around iteration 7.
@burmecia
Copy link
Member

burmecia commented Jan 7, 2026

thanks for the PR! Can you fix the clippy issue in CI?
image

@JohnCari
Copy link
Contributor Author

JohnCari commented Jan 9, 2026

@burmecia I will work on this today, tomorrow or during weekend but will be done by next week

Fix uninlined-format-args clippy warning by using inline variable
in format string: format!("stress_{i}") instead of format!("stress_{}", i)
@JohnCari
Copy link
Contributor Author

@burmecia All checks passed, fixed the clippy issues in CI.

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.

Postgres server process crash when inserting records to a foreign table.

2 participants