What is PL/Rust?

This documentation is under development.

PL/Rust is a loadable procedural language that enables writing PostgreSQL functions in the Rust programming language. These functions are compiled to native machine code. Unlike other procedural languages, PL/Rust functions are not interpreted.

The top advantages of PL/Rust include writing natively-compiled functions to achieve the absolute best performance, access to Rust's large development ecosystem, and Rust's compile-time safety guarantees.

PL/Rust is Open Source and actively developed on GitHub.

Features

PL/Rust provides access to Postgres' Server Programming Interface (SPI) including dynamic queries, prepared statements, and cursors. It also provides safe Rust types over most of Postgres built-in data types, including (but not limited to), TEXT, INT, BIGINT, NUMERIC, FLOAT, DOUBLE PRECISION, DATE, TIME, etc.

On x86_64 and aarch64 systems PL/Rust can be a "trusted" procedural language, assuming the proper compilation requirements are met. On other systems, it is perfectly usable as an "untrusted" language but cannot provide the same level of safety guarantees.

Example PL/Rust function

The following example shows an example PL/Rust function to count the length of an input string. See PL/Rust Functions and Arguments for more examples.

CREATE FUNCTION strlen(name TEXT)
    RETURNS int LANGUAGE plrust AS
$$
    Ok(Some(name.unwrap().len() as i32))
$$;

Using the function is just like any other PostgreSQL function.

SELECT strlen('Hello, PL/Rust');
┌────────┐
│ strlen │
╞════════╡
│     14 │
└────────┘

Built on pgrx

PL/Rust itself is a pgrx-based Postgres extension. Furthermore, each LANGUAGE plrust function are themselves mini-pgrx extensions. pgrxis a generalized framework for developing Postgres extensions with Rust. Like this project, pgrx is developed by TCDI.

The following sections discuss PL/Rusts safety guarantees, configuration settings, and installation instructions.

General Safety, by Rust

Quoted from the "Rustonomicon":

Safe Rust is the true Rust programming language. If all you do is write Safe Rust, you will never have to worry about type-safety or memory-safety. You will never endure a dangling pointer, a use-after-free, or any other kind of Undefined Behavior (a.k.a. UB).

This is the universe in which PL/Rust functions live. If a PL/Rust function compiles it has these guarantees, by the Rust compiler, that it won't "crash." This quality is important for natively-compiled code running in a production database.

What about unsafe?

PL/Rust uses the Rust compiler itself to wholesale disallow the use of unsafe in user functions. If a LANGUAGE plrust function uses unsafe it won't compile.

Generally, what this means is that PL/Rust functions cannot call unsafe fns, cannot call extern "C"s into Postgres itself, and cannot dereference pointers.

This is accomplished using Rust's built-in #![forbid(unsafe_code)] lint.

3rd-party crate dependencies are allowed to use unsafe. We'll discuss this below.

What about pgrx?

If pgrx is a "generalized framework for developing Postgres extensions with Rust", and if PL/Rust user functions are themselves "mini-pgrx extensions", what prevents a LANGUAGE plrust function from using any part of pgrx?

The plrust-trusted-pgrx crate does! The plrust-trusted-pgrx crate is a tightly-controlled "re-export crate" on top of pgrx that exposes the bare minimum necessary for PL/Rust user functions to compile along with the bare minimum, safe features of pgrx.

The crate is versioned independently to both pgrx and plrust and is published on crates.io. By default, the version a plrust user function will use is that of the one set in the project repository when plrust itself is compiled. However, the plrust.trusted_pgrx_version GUC can be set to specify a specific version.

The intent is that plrust-trusted-pgrx can evolve independently of both pgrx and plrust.

There are a few "unsafe" parts of pgrx exposed through plrust-trusted-pgrx, but PL/Rust's ability to block unsafe renders them useless by PL/Rust user functions. plrust-trusted-pgrx's docs are available on docs.rs.

What about Rust compiler bugs?

PL/Rust uses its own rustc driver which enables it to apply custom lints to the user's LANGUAGE plrust function. In general, these lints will fail compilation if the user's code uses certain code idioms or patterns which we know to have "I-Unsound" issues.

PL/Rust contains a small set of lints to block what the developers have deemed the most egregious "I-Unsound" Rust bugs.

Should new Rust bugs be found, and detection lints are developed for PL/Rust, the lints can be applied to new user function compilations along with ensuring that future function executions had those lints applied at compile time.

Note that this is done on a best-effort basis, and does not provide a strong level of security — it's not a sandbox, and as such, it's likely that a skilled hostile attacker who is sufficiently motivated could find ways around it (PostgreSQL itself is not a particularly hardened codebase, after all). You should ensure such actors cannot execute SQL on your database, but to be clear: this is true regardless of whether or not PL/Rust is installed. Having said that, any issues found with our implementation will be taken seriously, and should be reported appropriately.

Trusted with postgrestd on Linux x86_64/aarch64

The "trusted" version of PL/Rust uses a unique fork of Rust's std entitled postgrestd when compiling LANGUAGE plrust user functions. postgrestd is a specialized Rust compilation target which disallows access to the filesystem and the host operating system. The Install PL/Rust section outlines the steps required for trusted install of PL/Rust. Currently, postgrestd is only supported on Linux x86_64 and aarch64 platforms.

When plrust user functions are compiled and linked against postgrestd, they are prohibited from using the filesystem, executing processes, and otherwise interacting with the host operating system.

In order for PL/Rust to use postgrestd, its Rust compilation targets must be installed on the Postgres server. This happens via plrust's plrust/build script, which clones postgrestd, compiles it, by default, for both x86_64 and aarch64 architectures, and ultimately places a copy of the necessary libraries used by Rust for std into the appropriate "sysroot", which is the location that rustc will look for building those libraries.

The trusted Feature Flag

PL/Rust has a feature flag simply named trusted. When compiled with the trusted feature flag PL/Rust will always use the postgrestd targets when compiling user functions. Again, this is only supported on x86_64 and aarch64 Linux systems. postgrestd and the trusted feature flag are not supported on other platforms. As such, PL/Rust cannot be considered fully trusted on those platforms.

If the trusted feature flag is not used when compiling PL/Rust, which is the default, then postgrestd is not used when compiling user functions, and while they'll still benefit from Rust's general compile-time safety checked, forced usage of the plrust-trusted-pgrx crate, and PL/Rust's unsafe blocking, they will be able to access the filesystem and communicate with the host operating system, as the user running the connected Postgres backend (typically, this is a user named postgres).

PL/Rust is also a Cross Compiler

In this day and age of sophisticated and flexible Postgres replication, along with cloud providers offering Postgres on, and replication to, disparate CPU architectures, it's important that plrust, since it stores the user function binary bytes in a database table, support running that function on a replicated Postgres server of a different CPU architecture.

cross compilation has entered the chat

By default, PL/Rust will not perform cross compilation. It must be installed and enabled through configuration.

Configuring a host to properly cross compile is a thing that can take minimal effort to individual feats of heroic effort. Reading the (still in-progress) pgrx cross compile guide can help. Generally speaking, it's not too awful to setup on Debian-based Linux systems, such as Ubuntu. Basically, you install the "cross compilation toolchain" apt package for the other platform.