PL/Rust 1.0: now a trusted language for Postgres
tcdi.github.io
tcdi.github.io
Pl/pgsql, while not exactly elegant, is well suited to bridging that gap between imperative and set-oriented business logic within the database.
On the other hand, pl/pgsql is far from optimal for defining custom types and implementing operators for those types. For that, we've needed C.
But now the possibility opens up to create efficient custom types, operators, and other functions that can run at machine speed without depending on what a vendor decides to bundle or the mostly unvetted quality of some 3rd party's C code extension.
Potentially huge!
This means that you can write your database functions in rust, as an alternative to pgplsql / plv8.
(disclosure: i work at supabase)
https://tcdi.github.io/plrust/trusted-untrusted.html
_Normally, PL/Rust is installed as a "trusted" programming language named plrust. In this setup, certain Rust and pgx operations are disabled to preserve security. In general, the operations that are restricted are those that interact with the environment. This includes file handle operations, require, and use (for external modules). There is no way to access internals of the database server process or to gain OS-level access with the permissions of the server process, as a C function can do. Thus, any unprivileged database user can be permitted to use this language._
Languages like pl_python are "untrusted" and give too much access to the file system, which is why cloud providers never support them on their platforms
I do see that rust access to sendfile() would be via a syscall, which is in the unsafe category...so perhaps that's not the best example.
But it does make me curious how comprehensive a sandbox PL/Rust is providing, beyond just forbidding unsafe.
In Rust you'd normally be able to link c code, but calling c requires unsafe because you have to manually ensure the c code upholds any relevant Rust invariants.
> But it does make me curious how comprehensive a sandbox PL/Rust is providing, beyond just forbidding unsafe.
They also hook the compiler and try and detect shenanigans. It's not perfect, but it's pretty thought out.
*Technically the Rust standard library builds on top of a lower-level io module, which is all you have to replace.
https://tcdi.github.io/plrust/plrust.html#what-about-rust-co...
Rust keeps a list of soundness bugs via a tag on github - they're pretty common:
https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Ais...
As I read it, it seemed that the unsafe prohibition was more about the safety of the code and not the security of the system.
However, they make it clear that this is not intended to be your only defence against an attacker:
> 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
Surely all a "sufficiently motivated" attacker would need to do is peruse the unsound bugs on GitHub?
https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Ais...
Those aren't considered to be security issues. Makes me wonder what the point of banning `unsafe` is at all. You're going to need some other system anyway...
> The "trusted" version of PL/Rust uses a unique fork of Rust's std entitled postgrestd when compiling LANGUAGE plrust user functions.
If you a) don't have access to unsafe, and b) don't have access to the stdlib that lets you do powerful things without unsafe, then you're very limited in what you can do.
https://smallcultfollowing.com/babysteps/blog/2016/10/02/obs... discusses this further. Conceptually, you can think of "entirely Safe Rust" to be a very limited language, which you then progressively add "capabilites" to by exposing safe interfaces implemented with unsafe code. For example, Vec and Box (which require unsafe) grant safe code the ability to do heap allocations.
It's true that this is not designed as a security boundary. As I note in my comment above, the PL/Rust devs also make that clear. That doesn't mean it has no value as part of a defence in depth strategy.
I’m a big fan of PostgreSQL and it’s constantly an annoyance to find cool new capabilities provided by extensions I can never use since I’m not going to manage my own database in a critical environment for a lot of reasons… I’ve done it before, I know how hard it is to do well, and I don’t want this to be my job anymore… so when I find cool stuff like vector search or graph traversals but can never use them it’s just a constant disappointment.
Does this “trusted” state actually translate into greater adoption by cloud providers or is it just something the developers behind this effort hope will happen?
In terms of pl/rust becoming available on RDS and supabase: months. It’s going through security audits now
They've taken extra precautions to plug known holes, but it is using Rust beyond what it was designed for.
what is the use case?
Use to write PostgreSQL functions in Rust. Also
> 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.
Using a text oriented language like Perl with a good regexp engine might
DB performance, comes from indexes , table partitioning and in-memory tables and to compile query execution plans, so you save some time the very first you run a procedure
Smaller, more efficient types directly translate to less disk usage and smaller indexes, both of which measurably improve database performance.
The network round trip to the database can also be a pretty significant performant penalty, especially when iterating over large sets.
> Event Triggers and DO blocks are not (yet) supported by PL/Rust.
As far as event triggers and DO-blocks, that omission seems fine to me. Especially DO-blocks, which are essentially an inline code, one-off escape hatch in the middle of other SQL. Rust would not be helping any performance-sensitive critical paths in those cases.
https://www.postgresql.org/docs/current/sql-createtrigger.ht...
> The REFERENCING option enables collection of transition relations
I don't see any examples of statement triggers...
Note the use of new_table and old_table as aliases. Instead of single records in NEW and OLD, you can select against new and old sets of records.
PL/PGSQL is fine for "a bit more than a SELECT statement" and for very simple algorithms of <50 LOC. Anything more and please use a first class language like plrust, plv8, etc.
There are a lot of cases where plv8 will thrash back and forth between the internals of Postgres and C and its v8 engine. These are usually the cases where set theory dominates the solution space.
On the flip side, if you're doing a lot of filter/map/reduce on large JSON payloads, plv8 is demonstrably better than pl/pgsql.
Right tool. Right job.
The things I notice when working in PLPSQL:
* Ample boilerplate that needs to be correct when it could be inferred.
* Lack of a language server (doesn't help that PLPSQL is often embedded in strings in other files)
* Papercuts like procedures vs functions having different call syntaxes
* No/limited support for encapsulation
* No/limited package management
* Most new languages have syntactic sugar, like implicit returns / everything is an expression