编写简单的 PostgreSQL 拓展:以 Hello world 程序为例

编写简单的 PostgreSQL 拓展:以 Hello world 程序为例

文一

2024-08-07 发布76 浏览 · 0 点赞 · 0 收藏

编写简单的 PostgreSQL 拓展:以 Hello world 程序为例

学会编写 PostgreSQL 拓展可以在很大程度上促进我们了解内核的原理,进而帮助我们更好地发挥出 PostgreSQL 的潜能。

在本篇文章之中,我们将以编写一个简单的 Hello world 程序为例,就 PostgreSQL 拓展的组织结构,C语言函数的编写方案等展开讨论,期待能够为你带来有益的启示。

result

PostgreSQL 拓展的组织结构

一般而言,PostgreSQL 拓展都会整合为一个单独的文件夹(并且通过 git 工具进行源代码管理),它们都需要回答如下的三个问题:

  1. 如何描述这个拓展? 提供拓展的名称,版本号,简介内容,存储位置,使用编码等全局信息既能够促进 PostgreSQL 正确地部署我们的拓展,同时他们也将呈现给用户,用以帮助他们了解自家的数据库究竟在哪方面进行了增强。
  2. 拓展将会带来哪些功能? 拓展将会通过不同的 SQL 指令语句,向 PostgreSQL 注册自身所带来的改进内容,如使用 CREATE FUNCTION 在数据库中注册函数。
  3. 如何实现这个拓展? 所有的描述与注册最终都需要落地到实现上面,这部分内容便是往往就是拓展的工程源码。

而对这三个问题的回答即形成了如下的项目结构:

而如果我们希望编写好这个 pg_hello_world 拓展的话,我们可以首先创建一个空文件夹:

mkdir pg_hello_world

完成后,让我们首先编写拓展的描述文件,创建一个名为 pg_hello_world.control 的文件,内容如下:

# control 文件用于描述 PostgreSQL 拓展,# 的含义是注释
# 除了注释以外的内容用于描述数据,每一行描述一条数据,格式是
# 变量名 = 数据
# 更多相关信息可以参考: https://www.postgresql.org/docs/devel/extend-extensions.html#EXTEND-EXTENSIONS-FILES
comment = 'A PostgreSQL Extension Example'
default_version = '1.0'

再接下来,创建用于注册函数的 SQL 文件,内容如下:

/*
    CREATE FUNCTION 函数名(参数列表) RETURNS 变量类型
    AS '构建生成的面对对象文件', '函数名'
    LANGUAGE 实现用编程语言
    这只是一种注册的形式,详细形式可以参考:
    https://www.postgresql.org/docs/devel/sql-createfunction.html
*/
CREATE FUNCTION pg_hello_world() RETURNS CSTRING
AS 'pg_hello_world', 'pg_hello_world'
LANGUAGE C;

完成了这些基本的工作之后,我们将目光放在如何将拓展落地实现上,参考接下来的内容。

PostgreSQL 拓展的实现

PostgreSQL 的拓展自应用目的上面看,都是为了提升 PostgreSQL 处理关系型数据的能力,而这种拓展,大论其拓展方面,可划分为如下:

  1. 提供针对于某特定格式数据的类型拓展 如 PostGis 为 PostgreSQL 引入了诸多处理空间数据用的数据类型,进而使得我们可以便利地存储地理信息数据。
  2. 围绕某种特定格式数据,提供处理数据用函数 如 pgvector 中涵盖的诸多向量数据处理函数可以帮助我们展开数据分析用的计算。
  3. 对 PostgreSQL 语法格式的定制 如 ivorysql_ora 拓展引入的 Oracle 兼容性。
  4. 依托内核 Hook 机制,指导 PostgreSQL 内核的某种行为 这种指导往往便是对于 PostgreSQL 的深度定制,是多种拓展的综合应用,如 TimescaleDB 将 PostgreSQL 转变为了一款处理时序数据用的数据库。 Citus 则带来了分布式数据库的能力。

其它的周边工具的开发与设计,更多地应当放置于数据应用程序的开发上面,这一点我们此处不做展开。

pg_hello_world 在此处承担的职责,便是做了一个简单的反馈,它将会为我们提供一条形如下示的关系型数据:


(关系型数据便是二维表格数据,此处的数据可以理解为一张一行一列的数据表)

想要具体的实现它,我们需要编写一个C语言程序文件,此处命名为 pg_hello_world.c,文件内容如下所示:

/* 编写拓展所需要包含的一些头文件 */
#include "postgres.h"
#include "funcapi.h"
#include "fmgr.h"

PG_MODULE_MAGIC; /* PostgreSQL 拓展都需要这个宏展开 */

PG_FUNCTION_INFO_V1(pg_hello_world); /* 导出一个名为 pg_hello_world 的函数 */

Datum pg_hello_world(PG_FUNCTION_ARGS)
{
    /* 返回一条类型为 CSTRING 的关系型数据(这里需要同 SQL 文件中注册的返回类型相对应) */
    PG_RETURN_CSTRING("Hello world!"); 
}

在这个基础上,我们需要编写一个 makefile 文件,它负责自动化C语言文件的编译流程,并将他们整合到 PostgreSQL 之中。

# 这个 makefile 的正确运行需要 pg_config 程序的帮助,pg_config 是 PostgreSQL 的一个组成部分

# 编译并安装本拓展所需的基本信息(拓展名称、版本号、注册拓展信息所用 SQL 文件)

EXTENSION = pg_hello_world
EXTVERSION = 1.0
DATA = pg_hello_world--1.0.sql

# 编译后导出的拓展名称与依托的对象文件名称

MODULE_big = pg_hello_world
OBJS = pg_hello_world.o

# 下面的写法往往是固定的,作用是促使 pg_config 程序输出 pgxs 文件所在地址,进而为编译、安装、部署提供指导

USE_PGXS = 1
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

这样,pg_hello_world 即算基本完工,项目文件的基本形式如下:

最后,只需要通过执行如下的 shell 指令:

make install

再在 PostgreSQL 中执行:

CREATE EXTENSION pg_hello_world;
SELECT pg_hello_world();

就可以得到最终的结果。

常见问题

PG_MODULE_MAGIC 是什么?

它是一个宏,实际上是 PostgreSQL 为了更好的了解拓展兼容性,并保障其正常工作,所要求我们提供的一些程序基本信息。

(展开以后,可以发现 PG_MODULE_MAGIC 填充了某种数据结构的数据)

(对应数据结构的定义,可以发现,内容即是 PostgreSQL 的版本号、函数参数最大数目等信息)

PGXS 是什么?

PostgreSQL 所提供的一个内置 makefile 文件,它可以结合由 pg_config 程序所提供的信息,让拓展能够编译、部署到 PostgreSQL 所处的位置去。

(pg_config 所能够提供的部分信息,当主机上面存在多个 PostgreSQL 版本的时候,当哪一个 pg_config 程序提供了信息,拓展就会被安装到哪一个目录中去)

所以所有采用 PGXS 进行 makefile 设计的拓展都需要 pg_config 程序能够提供出正确的信息来(当然,对于正常部署的 PostgreSQL,它的工作结果都是正确的)。

Datum、PG_FUNCTION_ARGS 是什么?

他们都是 PostgreSQL 所提供的数据传输桥梁,因为拓展程序并非同 PostgreSQL 一同编译而产生,而是需要用动态加载的方式进入内核,因此自然而然需要用一定的手段,告知 PostgreSQL 我们所传输的数据内容,以及需要借助一定的手段,让我们能够获知来自于 PostgreSQL 的数据。

Datum 与 PG_FUNCTION_ARGS 便是为这种工作而设计的内容,其中前者是对于“数据类型”的抽象,用于代指其它所有的数据(正如 void* 在C语言指针中的位置一样),而后者则是对于用户调用函数时传入的参数的描述(拓展以后的内容非常复杂)。

Windows 下面编写拓展的思路是否类似?

实现思路是完全一样的,只是在编译工具的选择上会出现一定的区别,如 Windows 下我们所需要使用的动态库文件便不是 .o 而是 .dll,同时所使用的编译工具可能会是 nmake 等,所以此处的代码与 makefile 需要经过一定修改才可以在 Windows 上面运行。

写在最后

我们将这个程序发布到了 Mangrove 包管理器上面,欢迎通过 Mangrove 程序下载这款拓展。

期待更多的人参与到 PostgreSQL 拓展生态共建之中,目前国内的材料依旧是相当稀缺。