pgrx

repository·develop·Indexed 26 days ago

https://github.com/pgcentralfoundation/pgrx

A framework and Cargo subcommand (cargo-pgrx v0.19.1) for developing PostgreSQL extensions in Rust. It provides tools for project scaffolding, managing local Postgres instances, schema generation, and running in-process benchmarks and tests using #[pg_test] and #[pg_bench].

Tokens
56.1K
Snippets
133
Records
322
Agent score
86%

What's inside pgrx

  1. Overview of memory_contexts examples

    develop

    The memory_contexts package provides runnable examples demonstrating the PgMemoryContext lifecycle and how memory contexts interact with Set Returning Functions (SRFs) and background workers (bgworkers).

    Key examples include:

    • basics.rs: Demonstrates creating child PgMemoryContexts, using switch_to for scoped allocation, and using reset to reclaim memory. It also covers anti-patterns like use-after-reset.
    • srf_per_call.rs: Compares two SRFs (a streaming SetOfIterator and a materialized TableIterator) and explains the differences between multi_call_memory_ctx and per_query_ctx.
    • bgworker_state.rs: Shows a background worker that allocates long-lived state under TopMemoryContext.
  2. Overview of pgrx

    develop
    pgrx is a framework that allows developers to write PostgreSQL extensions using Rust instead of C. It leverages Rust's type system and procedural macros to handle complex Postgres-specific invariants, such as the function argument ABI and the generation of necessary SQL declarations (CREATE FUNCTION, CREATE TYPE). This approach aims to reduce the need for expert-level knowledge of C, SQL, and Postgres internals by providing safe abstractions.
  3. Understand Postgres Memory Contexts

    develop

    Postgres manages memory using "memory contexts" to prevent leaks. Most extension code runs in transient contexts that are automatically freed at the end of a transaction.

    Because the lifetime of an allocation is tied to its memory context, any Rust code returning an allocation must use appropriate lifetime parameters to prevent usage beyond the context's deallocation. In pgrx, the MemCx<'mcx> type is used to represent this relationship, binding the lifetime of the data to the lifetime of the memory context 'mcx.

  4. Explore pgrx examples

    develop

    The pgrx-examples directory provides practical implementations for various pgrx features. Use these examples to learn how to implement specific Postgres extension patterns in Rust, including:

    • Data Types: Working with arrays, bytea (as Vec<u8> or &[u8]), strings (text/varlena as String/&str), and composite_type (custom types backed by Rust structs/enums).
    • Postgres Integration: Using the Server Programming Interface (SPI), implementing Set-Returning-Functions (SRF), and managing Postgres schemas.
    • Advanced Extension Patterns: Implementing Background Workers (bgworker), creating custom Operators and Operator Classes, and managing Postgres Shared Memory (shmem).
    • Resource Management: Handling memory_contexts (lifecycle and SRF/bgworker patterns) and error handling (Postgres errors vs. Rust errors/panics).
    • Performance & Testing: Using in-process #[pg_bench] examples via cargo pgrx bench.
  5. Use pgrx-macros for procedural macros

    develop

    The pgrx-macros crate provides essential procedural macros and derive macros used to interface Rust code with PostgreSQL. If you are using pgrx as a dependency, you must also include pgrx-macros in your dependency list to enable these features.

    Available macros and derives include:

    • #[pg_extern]: To expose Rust functions to PostgreSQL.
    • #[pg_guard]: For safety/guarding logic.
    • #[pg_guc_hook]: For hooking into Grand Unified Configuration (GUC) settings.
    • #[pg_test]: For testing extension logic.
    • #[derive(PostgresType)]: To map Rust types to PostgreSQL types.
    • #[derive(PostgresEnum)]: To map Rust enums to PostgreSQL enums.
    • #[derive(PostgresGucEnum)]: To map Rust enums to PostgreSQL GUC enums.
  6. Understand the components of a PostgreSQL Aggregate

    develop

    A PostgreSQL aggregate is defined using CREATE AGGREGATE and typically relies on one or more supporting functions. The core components include:

    • SFUNC (State Function): The function that runs for each input item, taking the current state and the next value to produce a new state.
    • STYPE (State Type): The data type of the internal state maintained during aggregation.
    • INITCOND (Initial Condition): The starting value for the state (must be a string or null).
    • COMBINEFUNC (Combine Function): An optional function used for partial aggregation. It allows PostgreSQL to run multiple instances of the aggregate in parallel on subsets of data and then merge the results.
    • FINALFUNC (Final Function): An optional function that performs a final computation on the accumulated state to produce the ultimate result. This allows the return type to differ from the STYPE.

    Aggregates can also accept multiple arguments, which are passed together for each row processed.

  7. Understand pgrx FFI Error Handling

    develop

    pgrx manages the incompatible error handling models between Postgres (C) and Rust.

    • Postgres uses sigsetjmp/siglongjmp to jump across stack frames to rollback transactions. This can bypass Rust's Drop implementations, causing memory leaks.
    • Rust uses panics that unwind the stack or abort the process. Unwinding destroys Postgres's sigsetjmp checkpoints, and aborting shuts down the entire database.

    pgrx uses the #[pg_guard] macro to bridge these two worlds in two different directions: protecting Rust from Postgres ERRORs and protecting Postgres from Rust panic!s.

  8. Understand Rust destructor limitations with Postgres control flow

    develop

    Because pgrx operates within the Postgres environment, developers must be aware that Rust's Drop::drop implementations are not guaranteed to run. This is because Postgres can interrupt control flow in ways that prevent destructors from starting or finishing.

    To maintain safety, pgrx ensures that Rust control flow is independent of Postgres control flow. This is particularly relevant when dealing with non-trivial destructors, as the use of sigsetjmp and siglongjmp (used by pgrx to manage Postgres's machinations) can interact with Rust's execution model.