前言
Rust 语言是一门通用系统级编程语言,无GC且能保证内存安全、并发安全和高性能而著称。自2008年开始由 Graydon Hoare 私人研发,2009年得到 Mozilla 赞助,2010年首次发布 0.1.0 版本,用于Servo 引擎的研发,于 2015年5月15号发布 1.0 版本。 自发布以来,截止到2021 年的今天,经历六年的发展,Rust 得到稳步上升,已逐渐趋于成熟稳定。 至 2016 年开始,截止到 2021年,Rust 连续五年成为 StackOverflow 语言榜上最受欢迎的语言[1]。 2021年 2 月 9 号,Rust 基金会宣布成立。华为、AWS、Google、微软、Mozilla、Facebook 等科技行业领军巨头加入 Rust 基金会,成为白金成员,以致力于在全球范围内推广和发展 Rust 语言。
官方网如此介绍 Rust : 一门赋予每个人 构建可靠且高效软件能力的语言。 Rust 语言有三大优势值得大家关注:
1,高性能。Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务,可以在嵌入式设备上运行,还能轻松和其他语言集成。
2,可靠性。Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误。
3,生产力。Rust 拥有出色的文档、友好的编译器和清晰的错误提示信息, 还集成了一流的工具——包管理器和构建工具, 智能地自动补全和类型检验的多编辑器支持, 以及自动格式化代码等等。
Rust 和 C 都是硬件直接抽象
Rust 和 C 都是直接对硬件的抽象,都可看作一种「可移植汇编程序」。 Rust 和 C 都能控制数据结构的内存布局、整数大小、栈与堆内存分配、指针间接寻址等,并且一般都能翻译成可理解的机器代码,编译器很少插入 “魔法”。 即便 Rust 比 C 有更高层次的结构,如迭代器、特质(trait)和智能指针,它们也被设计为可预测地优化为简单的机器代码(又称 “零成本抽象”)。 Rust的类型的内存布局很简单,例如,可增长的字符串String 和 Vec 正好是{byte*, capacity, length}。Rust没有任何像 Cpp里的 移动 或 复制构造函数 这样的概念,所以对象的传递保证不会比传递指针或 memcpy 更复杂。
总的来说,Rust有媲美C的高性能,同时又具有高效的开发生产力,同时通过FFI可以高效的和其他语言如C进行混合编程或互相调用。本篇文章主要介绍如何利用Rust开发postgres extension。
部署开发环境
配置RUST开发环境
参考https://www.rust-lang.org/tools/install
IDE 推荐https://code.visualstudio.com/+https://rls.booyaa.wtf/
配置postgres开发环境
ubuntu
sudo apt-get install build-essential libreadline-dev zlib1g-dev flex bison libxml2-dev libxslt-dev libssl-dev libxml2-utils xsltproc
Red hat
sudo yum install -y bison-devel readline-devel zlib-devel openssl-devel wget
sudo yum groupinstall -y 'Development Tools'
cargo pgx 子命令安装
cargo install cargo-pgx
接下来执行命令cargo pgx init
Discovered Postgres v13.3, v12.7, v11.12, v10.17
Downloading Postgres v12.7 from https://ftp.postgresql.org/pub/source/v12.7/postgresql-12.7.tar.bz2
Downloading Postgres v13.3 from https://ftp.postgresql.org/pub/source/v13.3/postgresql-13.3.tar.bz2
Downloading Postgres v10.17 from https://ftp.postgresql.org/pub/source/v10.17/postgresql-10.17.tar.bz2
Downloading Postgres v11.12 from https://ftp.postgresql.org/pub/source/v11.12/postgresql-11.12.tar.bz2
Removing /home/wdy/.pgx/10.17
Untarring Postgres v10.17 to /home/wdy/.pgx/10.17
Configuring Postgres v10.17
Removing /home/wdy/.pgx/11.12
Untarring Postgres v11.12 to /home/wdy/.pgx/11.12
Untarring Postgres v12.7 to /home/wdy/.pgx/12.7
Configuring Postgres v11.12
Removing /home/wdy/.pgx/13.3
Untarring Postgres v13.3 to /home/wdy/.pgx/13.3
Configuring Postgres v12.7
Compiling Postgres v10.17
Configuring Postgres v13.3
Compiling Postgres v11.12
Compiling Postgres v12.7
Compiling Postgres v13.3
Installing Postgres v10.17 to /home/wdy/.pgx/10.17/pgx-install
Installing Postgres v11.12 to /home/wdy/.pgx/11.12/pgx-install
Installing Postgres v12.7 to /home/wdy/.pgx/12.7/pgx-install
Installing Postgres v13.3 to /home/wdy/.pgx/13.3/pgx-install
Validating /home/wdy/.pgx/10.17/pgx-install/bin/pg_config
Initializing data directory at /home/wdy/.pgx/data-10
Validating /home/wdy/.pgx/11.12/pgx-install/bin/pg_config
Initializing data directory at /home/wdy/.pgx/data-11
Validating /home/wdy/.pgx/12.7/pgx-install/bin/pg_config
Initializing data directory at /home/wdy/.pgx/data-12
Validating /home/wdy/.pgx/13.3/pgx-install/bin/pg_config
Initializing data directory at /home/wdy/.pgx/data-13
这个命令会下载版本v10,v11,v12,v13的postgres然后编译到目录~/.pgx/
中。这个下载步骤是必须的,因为后续pgx会为每中版本的postgres的header文件生成对应的Rust bindings,以及后续pgx 的测试框架中也会用到。
开发一个简单的extension
创建一个extension项目
使用下面命令创建一个名为my_extension的项目
cargo pgx new my_extension
命令执行后,会生成如下一个目录结构
├── Cargo.toml
├── my_extension.control
├── sql
│ ├── lib.generated.sql
│ └── load-order.txt
└── src
└── lib.rs
sql/lib.generated.sql 内容如下
CREATE OR REPLACE FUNCTION "hello_my_extension"() RETURNS text STRICT LANGUAGE c AS 'MODULE_PATHNAME', 'hello_my_extension_wrapper';
src/lib.rs内容如下
use pgx::*;
pg_module_magic!();
#[pg_extern]
fn hello_my_extension() -> &'static str {
"Hello, my_extension"
}
#[cfg(any(test, feature = "pg_test"))]
mod tests {
use pgx::*;
#[pg_test]
fn test_hello_my_extension() {
assert_eq!("Hello, my_extension", crate::hello_my_extension());
}
}
#[cfg(test)]
pub mod pg_test {
pub fn setup(_options: Vec<&str>) {
// perform one-off initialization when the pg_test framework starts
}
pub fn postgresql_conf_options() -> Vec<&'static str> {
// return any postgresql.conf settings that are required for your tests
vec![]
}
}
可以看到pgx已经默认给出了最简单的扩展实现。 #[pg_extern]
宏所修饰的函数就是我们要实现的extension函数,mod tests
, pub mod pg_test
是pgx已经为我们写好了的测试模块,用于编写相关测试代码。
pgx默认已经给我们写好了名为 hello_my_extension 的extension,功能很简单,就是返回 “Hello, my_extension” 字符串
运行extension
cd my_extension
cargo pgx run pg13 # or pg10 or pg11 or pg12
使用cargo pgx run 后跟参数pg13或pg10或pg11或pg12,对应不同的postgres版本,cargo pgx run会把extension编译为一个 .so 共享库文件,复制到对应版本的 ~/.pgx/ 目录中,然后启动Postgres实例,通过psql连接到和extension同名的数据库上。编译完成后,开发者就会处于psql的shell界面中,可以调用extension进行测试了。
这里我们执行 cargo pgx run pg12
得到输出如下:
$ cargo pgx run pg12
building extension with features `pg12`
"cargo" "build" "--features" "pg12" "--no-default-features"
Updating crates.io index
Downloaded generic-array v0.14.4
Downloaded humantime v2.1.0
Downloaded lazycell v1.3.0
Downloaded shlex v1.0.0
Downloaded stable_deref_trait v1.2.0
Downloaded termcolor v1.1.2
Downloaded typenum v1.13.0
Downloaded time-macros v0.1.1
Downloaded which v3