# The pgrust Query Executor Architecture: Safe Rust Implementation of PostgreSQL's Engine

> Explore the pgrust query executor architecture, a safe Rust implementation of PostgreSQL's engine. Learn how it eliminates C lifetime issues with an owned QueryDesc handle.

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

---

**The pgrust query executor reimplements PostgreSQL's execution engine in safe Rust using an owned `QueryDesc` handle that bundles all executor state, eliminating C-style lifetime issues while preserving the node-based plan execution model.**

The pgrust project (malisper/pgrust) provides a complete Rust-based reimplementation of PostgreSQL's backend, with its query executor architecture representing a fundamental shift from the original C implementation. By centralizing execution state within a single owned handle and leveraging Rust's memory safety guarantees, pgrust eliminates the scattered context objects and complex lifetime management that characterize the traditional PostgreSQL executor. This architecture maintains full compatibility with PostgreSQL's plan node system while providing modern memory safety and extension mechanisms.

## Core Architecture Components

### The QueryDesc Owner Handle

At the center of the pgrust executor architecture lies the **`QueryDesc`** struct, defined in [`crates/_support/types/nodes/src/querydesc.rs`](https://github.com/malisper/pgrust/blob/main/crates/_support/types/nodes/src/querydesc.rs). This structure serves as the single ownership handle for all executor state, replacing PostgreSQL's C-style approach of passing multiple pointers and context objects.

The `QueryDesc` contains:

- **`operation`** – the command type (SELECT, INSERT, UPDATE, DELETE)
- **`snapshot`** and **`crosscheck_snapshot`** – active read snapshots for MVCC
- **`dest`** – the destination receiver for result tuples
- **`params`** – external parameter bindings
- **`instrument_options`** – flags for query instrumentation
- **`already_executed`** – a guard preventing multiple `ExecutorRun` calls
- **`work`** – a `McxOwned<QueryWorkState>` bundle that owns the entire executor working set

The `work` field contains the critical execution state: `EStateData` (per-query executor state including memory contexts and plan counters), the immutable `PlannedStmt` produced by the planner, the original `source_text` for debugging, and the initialized `planstate` tree.

### Memory Management via McxOwned

Unlike PostgreSQL's C implementation that manually manages multiple memory contexts with complex lifetimes, pgrust wraps all per-query state in a **`McxOwned<QueryWorkState>`** bundle. This approach leverages Rust's ownership system to ensure that when the `QueryDesc` drops, the entire per-query memory context and all associated executor structures free in a single, deterministic operation.

## Query Execution Lifecycle

### Initialization with ExecutorStart

The execution lifecycle begins with **`CreateQueryDesc`**, implemented via `QueryDesc::create`. This method:

1. Allocates a dedicated per-query "ExecutorState" memory context
2. Instantiates an empty `EState` inside that context
3. Copies read-only inputs (`PlannedStmt`, source text, snapshots) into the owned bundle

The **`ExecutorStart`** phase, driven by the `execMain` seam implementation in [`crates/backend/executor/execMain/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/backend/executor/execMain/src/lib.rs), calls `ExecInitNode` on the top plan node. This recursively builds the full plan-state tree inside the same memory context, populating `QueryDesc::work.planstate` and establishing the result `TupleDesc`.

### Execution with ExecutorRun

The **`ExecutorRun`** phase implements the core driver loop. The executor repeatedly:

1. Pulls the next tuple from the plan tree via node-specific `ExecProcNode` implementations
2. Sends each tuple to the `DestReceiver` held in `QueryDesc::dest`

Individual plan nodes reside in dedicated crates such as `crates/backend/executor/nodeSeqscan`, `crates/backend/executor/nodeIndexscan`, and `crates/backend/executor/nodeModifyTable`. These modules implement the standard PostgreSQL `ExecProcNode` contract, but return `Result<PgResult<...>, PgError>` instead of using C's `longjmp` for error handling.

### Cleanup and Resource Management

Execution concludes with **`ExecutorEnd`** and **`FreeQueryDesc`**. When the `QueryDesc` handle drops, the `McxOwned` bundle automatically frees the per-query memory context and all associated structures, eliminating the risk of memory leaks or use-after-free errors common in the C implementation.

## Seam-Based Extensibility

### The execMain_seams Namespace

The pgrust executor implements a dense set of **seams** (hook points) collected under the `execMain_seams` namespace in [`crates/backend/executor/execMain/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/backend/executor/execMain/src/lib.rs). Each seam is a thin wrapper that external crates can override without modifying core executor code.

Key seams include:

- **`exec_check_permissions_select`** – permission validation before SELECT execution
- **`exec_foreign_update`** – foreign-data-wrapper update handling
- **`exec_partition_check`** – partition-specific constraint validation
- **`exec_init_result_rel`** – result-relation initialization for modifications

### Integration Examples

Extensions like `pg_stat_statements` and `plpgsql` hook into these seams to intercept execution events. For example, [`crates/pl/plpgsql/src/plpgsql_exec_seams/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/pl/plpgsql/src/plpgsql_exec_seams/src/lib.rs) consumes executor seams to integrate the PL/pgSQL interpreter, while [`crates/contrib/pg_stat_statements/src/store.rs`](https://github.com/malisper/pgrust/blob/main/crates/contrib/pg_stat_statements/src/store.rs) hooks into execution points to collect query statistics.

## Plan Node Implementation

Each plan node type lives in its own crate under `crates/backend/executor/`, maintaining the classic PostgreSQL node-based architecture while adding Rust's type safety. Nodes access the current executor state through the `execMain_seams` bundle, ensuring a single source of truth for the `EState` and plan-state tree.

The node implementations follow the standard `ExecProcNode` contract:

- **Initialization**: `ExecInitNode` builds the node-specific state
- **Execution**: `ExecProcNode` returns the next tuple or indicates completion
- **Cleanup**: `ExecEndNode` releases node-specific resources

## Practical Example

```rust
use pgrust::exec::execmain::Executor;
use pgrust::_support::types::nodes::QueryDesc;
use pgrust::_support::types::destreceiver::DestReceiverHandle;
use pgrust::_support::types::params::ParamListInfo;

// Create the owned query descriptor with all execution state
let mut qdesc = QueryDesc::create(
    &MemoryContext::Current,
    &planned_stmt,
    "SELECT id FROM my_table",
    None,
    None,
    DestReceiverHandle::null(),
    ParamListInfo::empty(),
    0,
).expect("failed to create QueryDesc");

// Execute the standard lifecycle
Executor::executor_start(&mut qdesc).expect("ExecutorStart failed");
Executor::executor_run(&mut qdesc, 0).expect("ExecutorRun failed");
Executor::executor_end(&mut qdesc).expect("ExecutorEnd failed");

// Cleanup occurs automatically when qdesc drops

```

This example demonstrates the high-level flow: create a `QueryDesc` that owns all state, run the executor lifecycle, and rely on Rust's drop mechanics for cleanup.

## Summary

- **Single ownership model**: The `QueryDesc` handle in [`crates/_support/types/nodes/src/querydesc.rs`](https://github.com/malisper/pgrust/blob/main/crates/_support/types/nodes/src/querydesc.rs) owns all executor state via `McxOwned<QueryWorkState>`, eliminating lifetime issues.
- **Safe error handling**: All executor functions return `Result` types instead of using C-style `longjmp`, preventing undefined behavior.
- **Seam-based extensions**: The `execMain_seams` namespace provides clean hook points for extensions without core code modification.
- **Node-based execution**: Individual crates like `nodeSeqscan`, `nodeIndexscan`, and `nodeModifyTable` implement the standard `ExecProcNode` contract in safe Rust.
- **Deterministic cleanup**: The `McxOwned` bundle ensures all per-query resources free automatically when execution completes.

## Frequently Asked Questions

### How does pgrust handle executor memory management differently from PostgreSQL?

PostgreSQL's C implementation uses multiple memory contexts with manual management and complex pointers-as-handles, requiring careful tracking of which context owns which object. Pgrust consolidates all per-query state into a single `McxOwned<QueryWorkState>` bundle owned by `QueryDesc`, leveraging Rust's ownership system to ensure automatic, deterministic cleanup when the handle drops.

### What are executor seams in pgrust?

Executor seams are hook points defined in the `execMain_seams` namespace that allow external crates to intercept and modify executor behavior. These seams wrap core operations like permission checks, foreign table updates, and result relation initialization, enabling extensions such as `pg_stat_statements` and `plpgsql` to integrate without modifying the executor's core logic.

### Which file defines the core execution handle in pgrust?

The core execution handle `QueryDesc` is defined in [`crates/_support/types/nodes/src/querydesc.rs`](https://github.com/malisper/pgrust/blob/main/crates/_support/types/nodes/src/querydesc.rs). This file contains the struct definition that owns the entire execution state, including the `EStateData`, `PlannedStmt`, and plan state tree, all wrapped in the `McxOwned<QueryWorkState>` bundle.

### How does pgrust handle errors in the executor without using longjmp?

Instead of PostgreSQL's `longjmp`-based error handling, pgrust node implementations return `Result<PgResult<...>, PgError>` types. This allows the executor to use Rust's standard error propagation mechanisms, ensuring that resources clean up properly through the `Drop` trait implementation on `QueryDesc` and its contained `McxOwned` bundle, even when errors occur during execution.