Home

Awesome

Logo

pgrx

Build Postgres Extensions with Rust!

cargo test --all crates.io badge docs.rs badge Twitter Follow Discord Chat

pgrx is a framework for developing PostgreSQL extensions in Rust and strives to be as idiomatic and safe as possible.

pgrx supports Postgres v11-v15.

Feel free to join our Discord Server.

Key Features

System Requirements

† PGRX has no MSRV policy, thus may require the latest stable version of Rust, available via Rustup

‡ A local PostgreSQL server installation is not required. cargo pgrx can download and compile PostgreSQL versions on its own.

<details> <summary>How to: GCC 7 on CentOS 7</summary>

In order to use GCC 7, install scl and enter the GCC 7 development environment:

yum install centos-release-scl
yum install devtoolset-7
scl enable devtoolset-7 bash
</details>

Getting Started

First install the cargo-pgrx sub-command and initialize the development environment:

cargo install --locked cargo-pgrx
cargo pgrx init

The init command downloads currently supported PostgreSQL versions, compiles them to ~/.pgrx/, and runs initdb. It's also possible to use an existing (user-writable) PostgreSQL install, or install a subset of versions, see the README.md of cargo-pgrx for details.

cargo pgrx new my_extension
cd my_extension

This will create a new directory for the extension crate.

$ tree 
.
├── Cargo.toml
├── my_extension.control
├── sql
└── src
    └── lib.rs

2 directories, 3 files

The new extension includes an example, so you can go ahead and run it right away.

cargo pgrx run

This compiles the extension to a shared library, copies it to the specified Postgres installation, starts that Postgres instance and connects you to a database named the same as the extension.

Once cargo-pgrx drops us into psql we can load the extension and do a SELECT on the example function.

my_extension=# CREATE EXTENSION my_extension;
CREATE EXTENSION

my_extension=# SELECT hello_my_extension();
 hello_my_extension
---------------------
 Hello, my_extension
(1 row)

For more details on how to manage pgrx extensions see Managing pgrx extensions.

Upgrading

You can upgrade your current cargo-pgrx installation by passing the --force flag to cargo install:

cargo install --force --locked cargo-pgrx

As new Postgres versions are supported by pgrx, you can re-run the pgrx init process to download and compile them:

cargo pgrx init

Mapping of Postgres types to Rust

Postgres TypeRust Type (as Option<T>)
byteaVec<u8> or &[u8] (zero-copy)
textString or &str (zero-copy)
varcharString or &str (zero-copy) or char
"char"i8
smallinti16
integeri32
biginti64
oidu32
realf32
double precisionf64
boolbool
jsonpgrx::Json(serde_json::Value)
jsonbpgrx::JsonB(serde_json::Value)
datepgrx::Date
timepgrx::Time
timestamppgrx::Timestamp
time with time zonepgrx::TimeWithTimeZone
timestamp with time zonepgrx::TimestampWithTimeZone
anyarraypgrx::AnyArray
anyelementpgrx::AnyElement
boxpgrx::pg_sys::BOX
pointpgrx::pgrx_sys::Point
tidpgrx::pg_sys::ItemPointerData
cstring&core::ffi::CStr
inetpgrx::Inet(String) -- TODO: needs better support
numericpgrx::Numeric<P, S> or pgrx::AnyNumeric
void()
ARRAY[]::<type>Vec<Option<T>> or pgrx::Array<T> (zero-copy)
int4rangepgrx::Range<i32>
int8rangepgrx::Range<i64>
numrangepgrx::Range<Numeric<P, S>> or pgrx::Range<AnyRange>
daterangepgrx::Range<pgrx::Date>
tsrangepgrx::Range<pgrx::Timestamp>
tstzrangepgrx::Range<pgrx::TimestampWithTimeZone>
NULLOption::None
internalpgrx::PgBox<T> where T is any Rust/Postgres struct
uuidpgrx::Uuid([u8; 16])

There are also IntoDatum and FromDatum traits for implementing additional type conversions, along with #[derive(PostgresType)] and #[derive(PostgresEnum)] for automatic conversion of custom types.

Note that text and varchar are converted to &str or String, so PGX assumes that any Postgres database you use it with has a UTF-8-compatible encoding. Currently, PGX will panic if it detects this is incorrect, to inform you, the programmer, that you were wrong. However, it is best to not rely on this behavior, as UTF-8 validation can be a performance hazard, this event was previously assumed to simply not happen, and PGX may decide to change the details of how it does UTF-8 validation in the future, as all functions for doing so are, in any case, fundamentally unsafe. For best results, always use PGX with UTF-8, and set database encodings explicitly upon database creation, as the default Postgres server encoding, SQL_ASCII, will guarantee neither ASCII nor UTF-8 (as Postgres will then accept but ignore non-ASCII bytes).

Digging Deeper

Caveats & Known Issues

There's probably more than are listed here, but a primary things of note are:

TODO

There's a few things on our immediate TODO list

Feature Flags

PGRX has optional feature flags for Rust code that do not involve configuring the version of Postgres used, but rather extend additional support for other kinds of Rust code. These are not included by default.

"unsafe-postgres": Allow compilation for Postgres forks that have a different ABI

As of Postgres v15, forks are allowed to specify they use a different ABI than canonical Postgres. Since pgrx makes countless assumptions about Postgres' internal ABI it is not possible for it to guarantee that a compiled pgrx extension will probably execute within such a Postgres fork. You, dear compiler runner, can make this guarantee for yourself by specifying the unsafe-postgres feature flag. Otherwise, a pgrx extension will fail to compile with an error similar to:

error[E0080]: evaluation of constant value failed
   --> pgrx/src/lib.rs:151:5
    |
151 | /     assert!(
152 | |         same_slice(pg_sys::FMGR_ABI_EXTRA, b"xPostgreSQL\0"),
153 | |         "Unsupported Postgres ABI. Perhaps you need `--features unsafe-postgres`?",
154 | |     );
    | |_____^ the evaluated program panicked at 'Unsupported Postgres ABI. Perhaps you need `--features unsafe-postgres`?', pgrx/src/lib.rs:151:5
    |

Contributing

We are most definitely open to contributions of any kind. Bug Reports, Feature Requests, Documentation, and even sponsorships.

If you'd like to contribute code via a Pull Request, please make it against our develop branch. The master branch is meant to represent what is currently available on crates.io.

Providing wrappers for Postgres' internals is not a straightforward task, and completely wrapping it is going to take quite a bit of time. pgrx is generally ready for use now, and it will continue to be developed as time goes on. Your feedback about what you'd like to be able to do with pgrx is greatly appreciated.

Hacking

If you're hacking on pgrx and want to ensure your test will run correctly, you need to have the current implementation of cargo-pgrx (from the revision you're working on) in your PATH.

An easy way would be to install cargo-local-install:

cargo install cargo-local-install

and then run cargo local-install to install cargo-pgrx as specified in top-level's Cargo.toml.

Don't forget to prepend /path/to/pgrx/bin to your PATH!

This approach can also be used in extensions to ensure a matching version of cargo-pgrx is used.

License

Portions Copyright 2019-2021 ZomboDB, LLC.  
Portions Copyright 2021-2023 Technology Concepts & Design, Inc.
Portions Copyright 2023 PgCentral Foundation, Inc.

All rights reserved.
Use of this source code is governed by the MIT license that can be found in the LICENSE file.