#postgresql #extension

pgx

pgx: 一个用于创建Postgres扩展的Rust框架

78次发布

0.7.4 2023年3月14日
0.7.2 2023年2月23日
0.6.1 2022年12月6日
0.6.0-alpha.22022年11月18日
0.0.8 2020年7月28日

数据库接口中排名第54位

Download history 226/week @ 2024-03-11 73/week @ 2024-03-18 73/week @ 2024-03-25 233/week @ 2024-04-01 95/week @ 2024-04-08 97/week @ 2024-04-15 136/week @ 2024-04-22 55/week @ 2024-04-29 100/week @ 2024-05-06 137/week @ 2024-05-13 115/week @ 2024-05-20 106/week @ 2024-05-27 84/week @ 2024-06-03 80/week @ 2024-06-10 62/week @ 2024-06-17 70/week @ 2024-06-24

每月下载量305
8 crates中使用

MIT许可证

10MB
338K SLoC

Logo

pgx

用Rust构建Postgres扩展!

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

pgx是一个用于在Rust中开发PostgreSQL扩展的框架,并力求在尽可能的方面保持惯用性和安全性。

pgx支持Postgres v11-v15。

欢迎加入我们的Discord服务器

主要功能

  • 一个完全管理的开发环境,包括cargo-pgx
    • cargo pgx new:快速创建新扩展
    • cargo pgx init:安装新的(或注册现有的)PostgreSQL安装
    • cargo pgx run:运行您的扩展并在psql(或pgcli)中进行交互式测试
    • cargo pgx test:跨多个PostgreSQL版本对您的扩展进行单元测试
    • cargo pgx package:为您的扩展创建安装包
    • 更多详情请查看README.md
  • 支持多个Postgres版本
    • 从相同的代码库支持Postgres v11-v15
    • 使用Rust功能门控来使用特定版本的API
    • 无缝测试所有版本
  • 自动模式生成
  • 安全优先
    • 将Rust的panic!转换为Postgres的ERROR,终止事务而不是进程
    • 内存管理遵循Rust的drop语义,即使在panic!elog(ERROR)

      的情况下也是如此。
    • 使用#[pg_guard]过程宏来确保上述内容
    • Postgres的DatumOption<T> where T: FromDatum
      • NULL Datum以安全的方式表示为Option::<T>::None
  • 第一类UDF支持
    • 使用#[pg_extern]注解函数以将其暴露给Postgres
    • 对于RETURNS SETOF,返回pgx::iter::SetOfIterator<'a, T>
    • 对于RETURNS TABLE (...),返回pgx::iter::TableIterator<'a, T>
    • 使用#[pg_trigger]创建触发函数
  • 易于自定义类型
    • 使用#[derive(PostgresType)]将Rust结构体用作Postgres类型
      • 默认情况下,在内存/磁盘上以CBOR编码的对象形式表示,并以JSON作为可读格式
      • 提供自定义的内存/磁盘/可读表示
    • 使用#[derive(PostgresEnum)]将Rust枚举用作Postgres枚举
    • 支持使用pgx::composite_type!("Sample")宏进行组合类型
  • 服务器编程接口(SPI)
    • 安全访问SPI
    • 从SPI上下文中透明地返回所有权的Datum
  • 高级功能
    • 通过pgx::PgMemoryContexts安全访问Postgres的MemoryContext系统
    • Executor/planner/transaction/subtransaction钩子
    • 安全使用Postgres提供的指针,与pgx::PgBox<T>(类似于alloc::boxed::Box<T>
    • #[pg_guard]宏用于保护需要传递给Postgres的Rust函数中的extern "C"
    • 通过类似于eprintln!的宏访问Postgres的日志系统
    • 通过pgx::pg_sys模块直接访问Postgres内部的大部分内容(不安全操作)
    • 定期添加新功能!

系统需求

  • Rust工具链:rustccargorustfmt。推荐从https://rustup.rs获取这些工具†
  • git
  • libclang 5.0或更高版本(bindgen需要)
    • Ubuntu:使用apt install libclang-devapt install clang
    • RHEL:使用yum install clang
  • tar
  • bzip2
  • GCC 7或更高版本
  • PostgreSQL的构建依赖项

† PGX没有MSRV策略,因此可能需要通过Rustup获取最新的稳定版Rust†

† 不需要本地PostgreSQL服务器安装。使用cargo pgx可以下载并编译PostgreSQL版本。

如何在CentOS 7上安装GCC 7

为了使用GCC 7,安装scl并进入GCC 7开发环境

yum install centos-release-scl
yum install devtoolset-7
scl enable devtoolset-7 bash

入门

首先安装cargo-pgx子命令并初始化开发环境

cargo install --locked cargo-pgx
cargo pgx init

init命令会下载当前支持的PostgreSQL版本,将它们编译到~/.pgx/,并运行initdb。也可以使用现有的(用户可写)PostgreSQL安装,或者安装版本子集,请参阅README.md中的详细说明

cargo pgx new my_extension
cd my_extension

这将创建一个用于扩展crate的新目录。

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

2 directories, 3 files

新扩展包括一个示例,因此您可以立即运行它。

cargo pgx run

这会将扩展编译为共享库,将其复制到指定的Postgres安装中,启动该Postgres实例并将您连接到与扩展同名的数据库。

一旦cargo-pgx将我们放入psql,我们就可以加载扩展并在示例函数上执行SELECT。

my_extension=# CREATE EXTENSION my_extension;
CREATE EXTENSION

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

有关如何管理pgx扩展的更多详细信息,请参阅管理pgx扩展

升级

您可以通过在cargo install中传递--force标志来升级您的当前cargo-pgx安装

cargo install --force --locked cargo-pgx

随着新版本的Postgres被pgx支持,您可以重新运行pgx init过程以下载和编译它们

cargo pgx init

Postgres类型到Rust的映射

Postgres类型 Rust类型(作为Option<T>
bytea Vec<u8>&[u8](零拷贝)
text String&str(零拷贝)
varchar String&str(零拷贝)或char
"char" i8
smallint i16
integer i32
bigint i64
oid u32
real f32
double precision f64
bool bool
json pgx::Json(serde_json::Value)
jsonb pgx::JsonB(serde_json::Value)
date pgx::Date
time pgx::Time
timestamp pgx::Timestamp
time with time zone pgx::TimeWithTimeZone
timestamp with time zone pgx::TimestampWithTimeZone
anyarray pgx::AnyArray
anyelement pgx::AnyElement
box pgx::pg_sys::BOX
point pgx::pgx_sys::Point
tid pgx::pg_sys::ItemPointerData
cstring &core::ffi::CStr
inet pgx::Inet(String) -- TODO: needs better support
numeric pgx::Numeric<P, S>pgx::AnyNumeric
void ()
ARRAY[]::<type> Vec<Option<T>>pgx::Array<T>(零拷贝)
int4range pgx::Range<i32>
int8range pgx::Range<i64>
numrange pgx::Range<Numeric<P, S>>pgx::Range<AnyRange>
daterange pgx::Range<pgx::Date>
tsrange pgx::Range<pgx::Timestamp>
tstzrange pgx::Range<pgx::TimestampWithTimeZone>
NULL Option::None
internal pgx::PgBox<T>其中T是任何Rust/Postgres结构体
uuid pgx::Uuid([u8; 16])

还有 IntoDatumFromDatum 特性,用于实现额外的类型转换,以及用于自动转换自定义类型的 #[derive(PostgresType)]#[derive(PostgresEnum)]

深入了解

注意事项和已知问题

可能还有更多,但值得关注的主要是

  • 线程支持不充分。Postgres 实行严格单线程。因此,如果您冒险使用线程,这些线程绝对不能调用任何内部 Postgres 函数,或者使用任何 Postgres 提供的指针。还有关于 Postgres 使用 sigprocmask 的潜在问题。这已经在 -hackers 列表中讨论过,甚至提供了一个补丁,但对话似乎已经停滞了(https://postgresql.ac.cn/message-id/flat/5EF20168.2040508%40anastigmatix.net#4533edb74194d30adfa04a6a2ce635ba)。
  • 如何在 async 上下文中正确与 Postgres 交互尚未探索。
  • pgx 包裹了大量的 unsafe 代码,其中一些安全性条件定义得不好。即使是从安全代码中,使用 pgx 也可能很容易引起不合理和不受欢迎的行为,并且其中一些包装可能根本不安全。请报告任何可能出现的问题。
  • 并非所有 Postgres 的内部组件都被包含或包装起来。这并非因为不可能,而是因为这是一项极其庞大的任务。如果您确定了需要使用的内部 Postgres API,请创建一个问题,我们将通过 pgx::pg_sys 模块公开它们。
  • 不支持 Windows。它可以,但需要与 cargo-pgx 一起进行一些工作,并找出如何编译 pgx 的 "cshim" 静态库。
  • ALTER EXTENSION my_extension UPDATE; 之前启动的会话将继续看到 my_extension 的旧版本。新会话将看到扩展的更新版本。
  • pgx 被许多 "生产环境" 使用,但它不是 "1.0.0" 或更高版本,尽管 SemVer 推荐生产质量软件使用该版本。这是因为还有许多未解决的安全性问题和用户体验问题,这些问题可能需要通过破坏性更改来解决,在某些情况下可能需要使用最前沿的 Rust 功能来公开安全的接口。虽然计划在某个时候发布 1.0.0 版本,但似乎明智地等待,直到看起来下周不需要 2.0.0 版本,并且剩余的问题可以延迟处理。

待办事项

我们的待办事项列表上有几件事情

  • 基于先前 git 标签和 HEAD 的差异,自动生成扩展架构升级脚本。很可能,这将构建到 cargo-pgx 子命令中,并使用 https://github.com/zombodb/postgres-parser
  • 更多示例 -- 尤其是关于内存管理和各种 derive 宏 #[derive(PostgresType/Enum)]

功能标志

PGX 为 Rust 代码提供了可选的功能标志,这些标志不涉及配置使用的 Postgres 版本,而是扩展了对其他类型 Rust 代码的支持。这些标志默认不包含在内。

"time-crate": 与 time crate 的互操作性

pgx 曾经使用与出色的 time crate 的直接互操作性。然而,由于性能和与 Postgres 精确互操作的问题,现在认为这个特性已被弃用,转而使用低开销的互操作性。您仍然可以通过启用 "time-crate" 功能来请求 TryFrom<time::Type> for pgx::MatchingTypeFrom<time::Type> for pgx::MatchingType 的实现。

"unsafe-postgres": 允许为具有不同 ABI 的 Postgres 分支进行编译

截至 Postgres v15,分支允许指定它们使用与规范 Postgres 不同的 ABI。由于 pgx 对 Postgres 内部 ABI 做了无数假设,因此它无法保证编译的 pgx 扩展可以在这样的 Postgres 分支中执行。您,尊敬的编译器运行者,可以通过指定 unsafe-postgres 功能标志来自行保证这一点。否则,pgx 扩展将无法编译并出现类似于的错误

error[E0080]: evaluation of constant value failed
   --> pgx/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`?', pgx/src/lib.rs:151:5
    |

贡献

我们绝对欢迎任何形式的贡献。错误报告、功能请求、文档,甚至 赞助

如果您想通过拉取请求贡献代码,请将其针对我们的 develop 分支。master 分支旨在表示当前在 crates.io 上的内容。

提供 Postgres 内部的包装器不是一项简单任务,并且完全包装它将花费相当多的时间。现在 pgx 通常已经准备好使用,并且随着时间的推移将继续开发。我们非常感谢您对 pgx 可以做什么的反馈。

黑客攻击

如果您正在对 pgx 进行黑客攻击并希望确保您的测试可以正确运行,您需要将当前 cargo-pgx(您正在工作的修订版)的实现放入您的 PATH

一种简单的方法是安装 cargo-local-install

cargo install cargo-local-install

然后运行 cargo local-install 以按顶级 Cargo.toml 中指定的方式安装 cargo-pgx

别忘了将 /path/to/pgx/bin 预先添加到您的 PATH

此方法还可以用于扩展,以确保使用匹配版本的 cargo-pgx

许可证

Portions Copyright 2019-2021 ZomboDB, LLC.  
Portions Copyright 2021-2022 Technology Concepts & Design, Inc. <support@tcdi.com>. 
All rights reserved.
Use of this source code is governed by the MIT license that can be found in the LICENSE file.

依赖关系

~7–11MB
~194K SLoC