# How the Seams Architecture Facilitates Porting PostgreSQL to Rust

> Discover how the Seams architecture in pgrust enables gradual PostgreSQL to Rust porting. Achieve test-driven migration and maintain binary compatibility with isolated Rust functions.

- Repository: [Michael Malis/pgrust](https://github.com/malisper/pgrust)
- Tags: architecture
- Published: 2026-07-13

---

**The seams architecture in pgrust isolates PostgreSQL's C implementation through small, ABI-compatible Rust functions that enable gradual, test-driven migration without breaking binary compatibility.**

The `malisper/pgrust` project implements a seam-based architecture to systematically port PostgreSQL from C to Rust. This approach allows developers to replace individual C functions with Rust implementations while keeping the server runnable and fully compatible with existing data directories. By defining stable contracts between legacy C code and new Rust logic, the architecture supports a step-by-step migration that preserves decades of battle-tested PostgreSQL behavior.

## What Is the Seams Architecture?

A **seam** is a small, well-defined Rust function that mirrors the signature and semantics of an existing C routine. Each seam is declared using the `seam_core::seam!` macro, which generates the necessary bindings to bridge the two languages.

For example, the signal handling seam in [`crates/port/port_pqsignal_seams/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/port/port_pqsignal_seams/src/lib.rs) declares a Rust implementation compatible with PostgreSQL's `pqsignal` function:

```rust
// crates/port/port_pqsignal_seams/src/lib.rs
seam_core::seam!(
    /// `void pqsignal(int signo, pqsigfunc func)` (`src/port/pqsignal.c`,
    /// symbol `pqsignal_be`) — install a signal handler via `sigaction` with
    /// `SA_RESTART` (plus `SA_NOCLDSTOP` for `SIGCHLD`). The backend variant
    /// returns `void` (unlike the legacy libpq `pqsignal`, which reports the
    /// previous disposition); a failing `sigaction(2)` is a coding error
    /// (`Assert(false)` in C), not an ereport, so the seam is infallible.
    pub fn pqsignal(signo: i32, func: signal::SigHandler)
);

```

When the C code is compiled, the build system generates a stub that forwards calls to the matching Rust seam. The Rust side implements the same signature and semantics, acting as a drop-in replacement that can be swapped without modifying surrounding C code.

## Gradual Migration Without Service Disruption

The seams architecture enables **incremental porting** by isolating each PostgreSQL C file into a corresponding seam module. Examples include `pg_crc32c_seams` for checksum algorithms, `pg_numa_seams` for NUMA-aware memory allocation, and `walsender_seams` for replication logic.

This modular approach delivers specific advantages:

- **Per-function replacement**: Developers can rewrite one routine at a time, compile the Rust crate, and link it into the existing PostgreSQL binary without touching unrelated subsystems.
- **Continuous operation**: The server remains runnable throughout the migration, allowing real-world testing at every stage.
- **Rollback capability**: If a Rust implementation introduces a regression, the build system can revert to the original C version by updating the seam stub.

## Maintaining Binary Compatibility

Seams expose the exact **C ABI**—including types and calling conventions—to ensure the Rust-produced binary remains compatible with existing PostgreSQL data directories. The linker resolves generated C wrapper symbols to the Rust implementations, producing a binary that boots against a standard PostgreSQL data directory without format changes or startup script modifications.

The build process follows three stages:

1. **Generate C stubs** – For each seam module, the system emits a small C wrapper that forwards calls to the Rust function via the `seam_core` runtime.
2. **Link** – The Rust crate links into the final PostgreSQL executable.
3. **Initialize** – At server startup, `init_seams()` registers all Rust implementations, overwriting default C behavior at runtime.

## Test-Driven Porting with Existing Suites

The architecture preserves PostgreSQL's existing regression test infrastructure. Because each seam maintains identical semantics to its C predecessor, the original PostgreSQL regression suite—comprising over 46,000 queries—runs unchanged against the seam-backed build. This guarantees functional parity while refactoring, catching subtle behavioral differences immediately rather than after months of development.

## Isolating Unsafe Code Boundaries

Memory safety improves dramatically through seam isolation. Only the seam stub itself uses `unsafe` blocks to bridge C and Rust, while the underlying implementation remains safe Rust. This pattern reduces the surface area for memory corruption bugs and makes security audits more manageable by concentrating `unsafe` code into thin, well-reviewed adapter layers.

## Runtime Implementation Swapping

Seams support **runtime replaceability**, allowing the system to switch between C and Rust implementations without recompilation. The `init_seams()` function determines at startup whether to use the original C function or a pure-Rust algorithm. This enables performance experiments—such as swapping the C `pg_crc32c` implementation for a Rust SIMD version—without rebuilding the entire server.

## Key Seam Components in pgrust

The pgrust repository organizes seams into specific crates that mirror PostgreSQL's internal structure:

- [`crates/port/port_pqsignal_seams/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/port/port_pqsignal_seams/src/lib.rs) – Signal handling seams for `pqsignal`
- [`crates/port/pg_crc32c_seams/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/port/pg_crc32c_seams/src/lib.rs) – CRC32C checksum routines
- [`crates/port/pg_numa_seams/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/port/pg_numa_seams/src/lib.rs) – NUMA-aware memory allocation
- [`crates/backend/replication/walsender_seams/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/backend/replication/walsender_seams/src/lib.rs) – WAL sender replication logic
- [`crates/common/jsonapi_seams/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/common/jsonapi_seams/src/lib.rs) – JSON API handling across the server
- [`crates/_support/pgrust/trace/README.md`](https://github.com/malisper/pgrust/blob/main/crates/_support/pgrust/trace/README.md) – Documentation of the seam runtime and initialization process

## Summary

- The seams architecture defines stable contracts between C and Rust through ABI-compatible function declarations using `seam_core::seam!`.
- Gradual migration proceeds one function at a time via modules like `pg_crc32c_seams` and `pg_numa_seams` without breaking server operation.
- Binary compatibility with existing PostgreSQL data directories is preserved through exact C ABI matching and linker-level integration.
- Over 46,000 existing regression tests validate functional parity throughout the porting process.
- Unsafe code is isolated to thin seam stubs, keeping the majority of the implementation in safe Rust.
- Runtime swapping via `init_seams()` enables performance testing of Rust implementations against C originals without recompilation.

## Frequently Asked Questions

### How does the seams architecture maintain PostgreSQL's binary compatibility during the Rust port?

The architecture exposes the exact C ABI—including types, calling conventions, and symbol names—through generated C stubs that forward calls to Rust implementations. According to the `malisper/pgrust` source code, these stubs allow the resulting binary to link against existing PostgreSQL data directories and startup scripts without modification, ensuring the Rust build boots a real PostgreSQL data directory.

### Can individual C functions be replaced without rebuilding the entire PostgreSQL server?

Yes. Each seam module isolates a specific C function or small group of functions. Developers implement the Rust version in crates like `crates/port/port_pqsignal_seams`, run `cargo build`, and link only that crate into the existing binary. The `init_seams()` function registers the new implementation at startup, enabling piecemeal migration without full recompilation.

### How does the architecture handle memory safety when bridging C and Rust code?

Only the seam stub uses `unsafe` blocks to interface with C, while the underlying Rust implementation remains safe code. This concentrates memory-unsafe operations into thin, auditable adapter layers, significantly reducing the attack surface compared to the original C codebase.

### What role do PostgreSQL's existing regression tests play in the porting process?

The original PostgreSQL regression suite runs unchanged against the seam-enabled build, executing over 46,000 queries to verify functional parity. Because seams mirror C function signatures exactly, these tests validate that Rust implementations behave identically to their C predecessors, catching regressions immediately during the port.