yihong0618 / gitblog

People Die, but Long Live GitHub
https://blog.yihong0618.me
MIT License
1.44k stars 135 forks source link

如何写一个 PostgreSQL Extension #270

Open yihong0618 opened 1 year ago

yihong0618 commented 1 year ago

什么是 PostgreSQL Extension

PostgreSQL Extension 是一个可插拔的功能扩展,用于在 PostgreSQL 数据库系统中添加额外的功能和能力。这些扩展可以由第三方开发者开发并加入到 PostgreSQL 中,以满足特定的需求。扩展可以增强数据库功能。 有些非常不错的 Extensions 甚至成了一些公司选型 Postgres 的理由比如:TimescaleDB, PostGIS 等等。

今年开始异常火爆的向量数据库,因为有 pgvector 也让 pg 有了向量计算和存储的能力。

本文会介绍如何编写 extensions 和推荐一些编写 Extensions 的资源。

如何写一个 Extensions

传统的 Extensions 开发一般情况下我们会 c 语言,引入 Postgers.h 写好 Makefile 和 SQL 开发,现在也有了用 Rust 编写 Extensions 的能力 -> pgrx 借助 pgrx 我们能更好的专注 extensions 中算法本身,也可以借助 Rust 强大的生态更容易的编写 extensions 需要的逻辑,进行更快速,更安全的开发。

以下分别介绍 C 和 Rust 两种开发方式。

C 语言开发 Extensions

以 hello_world 为例,需要在 hello_world 文件夹中建下面 4 个文件

hello.control         # 插件名.control
hello.c                   # 插件名.c
hello--1.0.sql        # 插件名--1.0.sql
Makefile                # 用于编译

cat hello.control

comment = 'hello:'
default_version = '1.0'
module_pathname = '$libdir/hello'
relocatable = false
superuser = true

剩下的可以参考这个 pg 的 hello_world 项目 https://github.com/magnusp/pg_hello

cat pg_hello.c

#include "postgres.h"

#include "fmgr.h"

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

Datum hello( PG_FUNCTION_ARGS );

PG_FUNCTION_INFO_V1( hello );
Datum
hello( PG_FUNCTION_ARGS )
{
   // variable declarations
   char greet[] = "Hello, ";
   text *towhom;
   int greetlen;
   int towhomlen;
   text *greeting;

   // Get arguments.  If we declare our function as STRICT, then
   // this check is superfluous.
   if( PG_ARGISNULL(0) ) {
      PG_RETURN_NULL();
   }
   towhom = PG_GETARG_TEXT_P(0);

   // Calculate string sizes.
   greetlen = strlen(greet);
   towhomlen = VARSIZE(towhom) - VARHDRSZ;

   // Allocate memory and set data structure size.
   greeting = (text *)palloc( greetlen + towhomlen );
   SET_VARSIZE(greeting, greetlen + towhomlen  + VARHDRSZ);

   // Construct greeting string.
   strncpy( VARDATA(greeting), greet, greetlen );
   strncpy( VARDATA(greeting) + greetlen,
            VARDATA(towhom),
            towhomlen );

   PG_RETURN_TEXT_P( greeting );
}

代码的一些解释:

  1. #include "postgres.h" 包含了大部分编写 postgres 相关程序需要的东西,每个 extensions 必须包含这个
  2. #include "fmgr.h" 则包含了PG_GETARG_XXX、PG_RETURN_XXX和PG_ARGISNULL 等编写 extensions 必要的宏
  3. Datum 是 data 的单数是在 pg 中最重要的数据类型之一,它肩负着在PG 内核与用户代码之间传递数据的责任
  4. PG_MODULE_MAGIC 这个宏是编写 extensions 的一个必要的宏为了后面编译生成的库才可以被 postgresql 加载

在这之后就可以编写好 Makefile 把 pg 的 PATH 加入到环境变量中之后 make && make install

在这之后进入到 psql, create extension hello; select hello('hello'); 就可以了

当然后续可以写一些测试,需要建一个文件夹名为 sql 把相关的测试写在里面,再建一个文件夹名为 expected 把测试跑出的结果写在里面,在 Makefile 加上这句 REGRESS = hello, 就可以利用 pg 的 installcheck 了

cat sql/hello.sql


CREATE EXTENSION hello;
select hello_hello();

如果不知道如何写插件可以参考 pg 核心开发者这个项目,里面有各种各样的 extensions

Rust 语言开发 Extensions

当然现在是 2023 年我们完全可以借助 Rust 来开发 Extensions, 这特别得益于 pgrx 这个项目它的前身也是一个非常棒的 pg 插件 zombodb

来看看借助 pgrx 开发的优秀的 pg 插件

编写起来就比 C 简单多了,只需要

  1. 安装 pgrx
  2. cargo pgrx init
  3. cargo pgrx new hello
  4. cargo pgrx install or cargo prgx run

一个简单的 hello 程序就做好了,其中 cargo pgrx new hello 帮助我们生成了所有需要的文件,其中 pgrx-examples 文件夹中包含了很多如何使用 pgrx 编写的例子。

当然大家可以参考这个项目 pg_slugify,利用了 Rust 的生态,几行代码做了一个非常有用 extension, 大家的很多简单的脚本完全可以利用 Rust 编写成插件方便自己开发。

推荐一些资源

补充

2024.11.07 我用 pgrx 写了两个 extensions https://github.com/yihong0618/pg_polyline https://github.com/yihong0618/pg_geohash

wey-gu commented 1 year ago

真棒👍🏻

tao12345666333 commented 1 year ago

这样看的话,用 Rust 写扩展是更简单 & 推荐的方式?

yihong0618 commented 1 year ago

这样看的话,用 Rust 写扩展是更简单 & 推荐的方式?

对于熟悉 Rust 不太了解 pg 插件开发的朋友,是。