PostgreSQL扩展开发入门

PostgreSQL 是一个功能强大的开源关系数据库管理系统。 它使用附加功能扩展了 SQL 语言。 DBMS 的定义不仅在于其性能和开箱即用的功能,还在于其支持定制/附加用户特定功能的能力。 其中一些功能可能采用数据库结构或模块的形式,如存储过程或函数,但它们的范围通常仅限于 DBMS 公开的功能。 例如,将如何编写驻留在你的 DBMS 中的自定义查询分析应用程序?

为了支持这些选项,PostgreSQL 提供了一个可插入的架构,允许你安装扩展(extension)。 扩展可能包括配置(控制)文件、SQL 文件的组合和动态可加载的库。

这意味着你可以根据扩展的定义指南编写自己的代码并将其插入 PostgreSQL 实例,而无需更改实际的 PostgreSQL 代码树。 根据定义,扩展扩展了 PostgreSQL 的功能,但不仅如此,它还使你能够与外部实体进行交互。 这些外部实体可以是其他数据库管理系统,如 ClickHouse、Mongo 或 HDF(通常称为外部数据包装器),或其他解释器或编译器(因此允许我们用另一种语言编写数据库功能,如 Java、Python、Perl 或 TCL, ETC。)。 扩展的另一个潜在用例是代码混淆,它允许你保护你的超级机密代码免遭窥探。

1、开发自己的PostgreSQL扩展

要构建自己的扩展,不需要完整的 PostgreSQL 代码库。 可以使用已安装的 PostgreSQL 构建和安装扩展(它可能需要您安装 devel RPM 或 Debian 软件包)。 有关扩展的详细信息,请参阅 PostgreSQL 的官方文档。 在 PostgreSQL 源代码的 contrib 目录中有许多可用于不同功能的扩展。 除了 contrib 目录之外,人们还在编写可在 Internet 上轻松获得但目前不属于 PostgreSQL 源代码树的扩展。 pg_stat_statements、PL/pgSQL 和 PostGIS 是最著名或使用最广泛的扩展示例。

一般可用的 PostgreSQL 扩展可以分为四个主要类别:

  • 添加对新语言扩展的支持(PL/pgSQL、PL/Python 和 PL/Java)
  • 可以在其中引入新的数据类型扩展((Hstore、cube 和 hstore)
  • 杂项扩展(contrib文件夹有很多杂项扩展)
  • 外部数据包装器扩展(postgres_fdw、mysqldb_fdw、clickhousedb_fdw)

构建扩展需要四种基本文件类型:

  • Makefile:它使用 PGXS PostgreSQL 的扩展构建基础设施。
  • 控制文件:携带有关扩展名的信息。
  • SQL 文件:如果扩展有任何 SQL 代码,它可能驻留在表单 SQL 文件中(可选)
  • C 代码:我们要构建的共享对象(可选)。

2、PostgreSQL扩展的makefile

要编译 C 代码,我们需要一个 makefile。 这是一个非常简单的 makefile,除了“PGXS”,它是 PostgreSQL 用于创建扩展的基础 makefile。 “PGXS”的包含是通过调用带有“–pgxs”标志的 pg_config 二进制文件来完成的。 该文件的详细信息可以在 GitHub上找到。

这是一个示例 makefile,可用于编译 C 代码。

EXTENSION = log
MODULE_big = log
DATA = log--0.0.1.sql
OBJS = log.o 
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

3、PostgreSQL扩展的控制文件

此文件必须命名为  [EXTENSION NAME].control。 控制文件可以包含许多选项,这些选项可以在官方文档 中找到。 但在这个例子中,我使用了一些基本选项。

  • comments:关于扩展的注解。
  • default_version:这是扩展的 SQL 版本。 SQL 文件的名称在文件名中包含此信息。
  • relocatable:是否可以将包含的对象移动到不同的架构(schema)中。
  • module_pathname:该信息被替换为实际的lib文件路径。
comment = 'PostgreSQL Utility Command Logger
'default_version = '0.0.1'
relocatable = true
module_pathname = '$libdir/log'

可以使用 psql 命令 \dx psql 查看文件的内容。

postgres=# \dx log      
List of installed extensions Name   | Version | Schema |       Description 
------------------------------------+---------+--------+---------------------------------- 
log                                 | 0.0.1   | public | PostgreSQL Utility Command Logger

4、PostgreSQL扩展的 SQL 文件

这是一个映射文件,我用它来将 PostgreSQL 函数映射到相应的 C 函数。 无论何时调用 SQL 函数,都会调用相应的 C 函数。 文件名必须是 [EXTENSION NAME]–[default-version].sql。 这与控制文件中定义的 default_version 相同。

CREATE FUNCTION pg_all_queries(OUT query TEXT, pid OUT TEXT)
RETURNS SETOF RECORDAS 'MODULE_PATHNAME', 
'pg_all_queries'
LANGUAGE C STRICT VOLATILE;

5、PostgreSQL扩展的 C 代码

可以用 C 代码编写三种函数。

第一个是使用在扩展的 SQL 文件中编写的 SQL 函数调用 C 代码函数的地方。

第二种类型的函数是回调。 通过分配函数的指针来注册该回调。 这里不需要 SQL 函数。 当特定事件发生时,会自动调用此函数。

第三类函数是自动调用的,甚至不需要注册。 这些函数在诸如扩展加载/卸载时间等事件上被调用。

这是包含 C 代码定义的 C 文件。 C 文件的名称或数量没有限制。

#include "postgres.h"
/* OS Includes */
/* PostgreSQL Includes */
PG_MODULE_MAGIC;
void _PG_init(void);
void _PG_fini(void);
Datum pg_all_queries(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(pg_all_queries);
static void process_utility(PlannedStmt *pstmt, const char *queryString,ProcessUtilityContext context,ParamListInfo params,QueryEnvironment *queryEnv,DestReceiver *dest,       char *completionTag);

你需要为扩展包含 postgres.h。 可以根据需要包括其他 PostgreSQL。 PG_MODULE_MAGIC 是一个宏,你需要将其包含在 C 文件中以进行扩展。 _PG_init 和 _PG_fini 分别是加载或卸载扩展时调用的函数。

下面是一个扩展的加载和卸载函数的示例。

void _PG_init(void)
{
    /* ... C code here at time of extension loading ... */
    ProcessUtility_hook = process_utility;
}

Void _PG_fini(void)
{
    /* ... C code here at time of extension unloading ... */
}

下面是一个回调函数的示例,可以在调用实用程序语句时调用该回调函数,例如 任何 DDL 语句。 “queryString”变量包含实际的查询文本。

static void process_utility(PlannedStmt *pstmt, 
                           const char *queryString,
                           ProcessUtilityContext context,
                           ParamListInfo params,
                           QueryEnvironment *queryEnv,DestReceiver *dest,
                           char *completionTag)
{
    /* ... C code here ... */
    standard_ProcessUtility(pstmt,   
                            queryString,   
                            context,   
                            params,   
                            queryEnv,   
                            dest, 
                            completionTag);
    /* ... C code here ... */
}

最后是一个通过用户定义的 SQL 函数调用的 C 函数示例。 这会在内部调用共享对象中包含的 C 函数。

Datum pg_all_queries(PG_FUNCTION_ARGS)
{
    /* ... C code here ... */
    tupstore = tuplestore_begin_heap(true, false, work_mem);
    /* ... C code here ... */     
    values[0] = CStringGetTextDatum(query);
    values[1] = CStringGetTextDatum(pid);
    /* ... C code here ... */
    tuplestore_donestoring(tupstore);
    return (Datum) 0;
}

6、PostgrepSQL扩展的编译安装

编译前,如果系统路径中没有pg_config,需要设置PostgreSQL的bin目录的PATH:

export PATH=/usr/local/pgsql/bin:$PATH

构建PostgrepSQL扩展:

make USE_PGXS=1

安装PostgrepSQL扩展:

make USE_PGXS=1 install

7、PostgreSQL扩展的输出

我们现在可以通过一个简单的 SQL 查询来使用我们的扩展。 以下是直接来自用 C 编程语言编写的扩展的输出。

postgres=# select * from pg_all_queries();          
query                      | pid 
--------------------------+|------ 
create table foo(a int);  +| 8196 
create table bar(a int);  +| 8196 
drop table foo;           +| 8196 
(3 rows)

8、结束语

我希望这个示例可以作为你创建更多有用的PostgreSQL扩展的起点,这些扩展不仅可以帮助您和您的公司,还可以让你有机会分享和帮助 PostgreSQL 社区发展。

完整的示例可以在 Github上找到。


原文链接:Writing PostgreSQL Extensions is Fun – C Language

BimAnt翻译整理,转载请标明出处