PL/pgSQL Executor Implementation in malisper/pgrust: Understanding REAL-OR-LOUD

The PL/pgSQL executor implementation in malisper/pgrust is a line-by-line Rust port of PostgreSQL's pl_exec.c that uses a REAL-OR-LOUD discipline to separate fully ported control-flow logic from intentionally panicking value-substrate seams.

The malisper/pgrust project reimplements PostgreSQL's procedural language infrastructure in Rust, located in the crates/pl/plpgsql/src/exec directory. This article examines how the PL/pgSQL executor implementation recreates the original C execution skeleton while tracking porting progress through a strict binary classification system.

What Is the PL/pgSQL Executor Implementation?

The executor serves as the runtime engine for compiled PL/pgSQL functions. It traverses the PLpgSQL_function abstract syntax tree and dispatches to handlers for every statement type—including IF, CASE, LOOP, WHILE, FOR, FOREACH, EXIT, and RETURN nodes.

As implemented in malisper/pgrust, the executor mirrors the exact arm order, SQLSTATE messages, and return-code propagation (LOOP_RC_PROCESSING) from the original PostgreSQL C file pl_exec.c. The implementation preserves the original control-flow skeleton while replacing C-specific mechanisms like longjmp/PG_TRY with Rust-native error channels.

The REAL-OR-LOUD Porting Philosophy

The REAL-OR-LOUD discipline governs every boundary between the ported Rust code and the underlying PostgreSQL value system. Each seam must be classified as either fully functional (REAL) or deliberately unimplemented (LOUD).

REAL Components (Control-Flow Logic)

REAL designates code that is fully ported 1:1 from C. This includes the statement-dispatch functions (exec_stmt_*), loop handling mechanics, block exception sub-transactions, and the error-propagation channel that replaces C's exception handling. When execution flows through REAL code, it behaves identically to the original PostgreSQL implementation, processing control structures without calling into unported value-level operations.

LOUD Seams (Value Substrate)

LOUD marks the "value substrate"—every operation that evaluates an expression, executes a query via SPI, reads or writes a Datum, iterates arrays, or deconstructs composite types. These operations route through seams that invoke panic! (i.e., fail loudly) and embed the original C callee name and subsystem (such as executor ExprState, plan-based SPI surfaces, or array and fmgr substrates). In a production C build, these paths would trigger ereport or elog; in pgrust they crash immediately to surface missing ports during testing.

The Binary Classification Rule

According to src/exec/src/seam.rs and src/handler/src/seam.rs, each seam must follow the REAL-OR-LOUD rule: the functionality is either complete (REAL) or it panics (LOUD). This binary classification eliminates ambiguous partial implementations and forces any missing behavior to be explicit and observable during test execution.

Executor Architecture and Key Functions

The executor operates through three distinct layers that bridge the REAL control flow with LOUD value operations.

Inward Seams and Callback Registration

The inward seam layer installs compile-time callbacks such as plpgsql_exec_get_datum_type_info via init_seams. These hooks allow the executor to query type information without directly accessing unported catalog functions, maintaining the boundary between REAL logic and LOUD substrate.

Core Execution Functions

The execution core implements the statement dispatcher and block handler:

  • exec_toplevel_block – Entry point for executing a compiled PL/pgSQL function, initializing the PLpgSQL_execstate context
  • exec_stmt_block – Processes block-level statements, handling variable scoping and exception trapping
  • loop_rc_processing – Manages the LOOP_RC_PROCESSING return-code table that tracks exit conditions for loop control structures

These functions reside in crates/pl/plpgsql/src/exec/lib.rs and constitute the REAL skeleton that orchestrates statement execution.

Outward Seams and Panic Behavior

Value-level operations flow through outward seams defined in crates/pl/plpgsql/src/exec/seam.rs. Any call that touches the SQL executor, SPI, or datum handling routes through these seams. For example, calling seam::exec_eval_expr(expr, estate) evaluates an expression through the SPI bridge, but will panic if the underlying C implementation is not yet provided.

Error Handling Without longjmp

The Rust executor replaces PostgreSQL's longjmp/PG_TRY exception mechanism with structured error propagation. Instead of unwinding the C stack via longjmp, the executor uses Rust's Result types and error channels to propagate PgError values upward through exec_toplevel_block and related functions. This preserves the original error semantics while using memory-safe Rust patterns.

Key Source Files in the Implementation

The executor spans several crates that separate concerns between control flow, seam definitions, and trigger handling:

Code Examples

Executing a Top-Level Block

To execute a compiled PL/pgSQL function, initialize the execution state and invoke the top-level block handler:

let mut estate = PLpgSQL_execstate::new(...);
let block = compiled_function.stmts[0].as_block(); // PLpgSQL_stmt_block
let result = plpgsql_exec::exec_toplevel_block(&mut estate, block)?;
// `result` is PLpgSQL_rc_result – either OK or an Err(PgError)

Installing a REAL Seam

Compile-time callbacks can be installed to provide REAL implementations for specific operations:

plpgsql_exec_seams::install_get_datum_type_info(|param| {
    // REAL implementation of make_datum_param
});

Encountering a LOUD Seam

When execution hits unported value logic, the seam panics with diagnostic information:

let datum = seam::exec_eval_expr(expr, estate)?;   // panics if the SPI bridge is not ported

Summary

  • The PL/pgSQL executor implementation in malisper/pgrust is a faithful Rust recreation of PostgreSQL's pl_exec.c, located in crates/pl/plpgsql/src/exec
  • REAL code handles control flow (loops, conditionals, blocks) and is fully ported from C
  • LOUD seams handle value operations (SPI, Datums, expressions) and deliberately panic to expose unimplemented functionality
  • The REAL-OR-LOUD binary classification eliminates ambiguity and forces explicit porting status
  • Core functions like exec_toplevel_block, exec_stmt_block, and loop_rc_processing form the REAL execution skeleton
  • Error propagation replaces C's longjmp with Rust Result types and error channels

Frequently Asked Questions

What is the REAL-OR-LOUD pattern in pgrust?

REAL-OR-LOUD is a binary classification system where every seam in the PL/pgSQL executor must be either REAL (fully ported from C and functional) or LOUD (intentionally panicking to mark unimplemented value-level operations). This pattern appears in src/exec/src/seam.rs and ensures that missing functionality crashes loudly during testing rather than failing silently or behaving unexpectedly.

How does the Rust executor handle errors without C's longjmp?

The executor replaces PostgreSQL's longjmp/PG_TRY mechanism with Rust-native error propagation using Result types and error channels. According to the source in crates/pl/plpgsql/src/exec/lib.rs, the error-propagation channel carries PgError values upward through the call stack, allowing exec_toplevel_block to return execution status without invoking C-style stack unwinding.

Which PL/pgSQL features are currently REAL versus LOUD?

REAL features include the statement dispatcher (exec_stmt_* functions), all loop constructs (handling via loop_rc_processing), block-level exception management, and control-flow logic. LOUD features cover the value substrate: expression evaluation through seam::exec_eval_expr, SPI query execution, Datum read/write operations, array iteration, and composite type deconstruction. Any operation touching these value layers routes through seams that panic if the underlying C bridge is absent.

Where is the PL/pgSQL executor code located in the repository?

The executor implementation resides in the crates/pl/plpgsql/src/exec/ directory. The main control flow lives in lib.rs, seam definitions are in seam.rs, and supporting modules include trigger.rs for DML triggers and mem.rs for memory management. Seam registration and management occur in crates/pl/plpgsql/src/plpgsql_exec_seams/src/lib.rs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →