如何将PostgreSQL插件移植到openGauss

1 概述

PostgreSQL社区提供了丰富的插件,但由于openGauss和PostgreSQL存在一定的差异,如线程/进程模型、系统表和视图等,无法直接为openGauss所用,不可避免的需要在插件上做整改。

本文档主要对Postgresql插件移植到openGauss的过程提供指导说明,旨在让开发人员对PG插件所需要的修改有一个具体的了解,基于该文档,可基本实现PG插件移植到openGauss。

2 约束

由于openGauss与PostgreSQL在内核上存在不少差异,这篇文档未能覆盖所有这些差异,因此仅依赖该文档有可能无法实现PG插件的完全迁移,部分差异需要开发者深入内核源码识别,然后可将识别出来的差异补充到该博客的第9章对应小节的表格中(博客对应的gitee地址:https://gitee.com/opengauss/blog/blob/master/content/zh/post/chenxiaobin/,具体操作可见blog仓库的将PostgreSQL插件移植到openGauss指导.md ),有任何问题可在博客下方留言讨论。

3 移植步骤

1) 将PG插件的代码拷贝到openGauss源码的contrib目录下

2) 配置环境变量,需要将数据库的bin和lib加在操作系统的环境变量PATH和LD_LIBRARY_PATH中

3) 到插件目录下,执行make && make install,编译安装插件。

4) 编译成功后,到数据库中执行create extension extension_name即可使用。

通常步骤3和4不会直接成功,需要一些必须的修改。下面分类别说明移植PG插件所需要做的修改。

4 Makefile文件

1) 当前有两种方式支持插件编译,一种是依赖源码编译,一种是用pgxs的方式编译,支持插件在一个已经安装的数据库服务上进行编译。建议选择前者的方式,如果采用后者,需要定义USE_PGXS,但是可能出现部分头文件找不到的问题,这时候需要到源码拷贝头文件到目标目录。

ifdef USE_PGXS

PG_CONFIG = pg_config 

PGXS := $(shell $(PG_CONFIG) --pgxs) 

include $(PGXS) 

else 

subdir = contrib/pg_freespacemap 

top_builddir = ../.. 

include  $(top_builddir)/src/Makefile.global 

include  $(top_srcdir)/contrib/contrib-global.mk 

endif

2) -fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code)。使用-fPIC,可以使得动态库可以被多个程序共享。不加fPIC加载的so,要在加载时根据加载到的位置再次重定位。

override CPPFLAGS :=$(filter-out -fPIE,  $(CPPFLAGS)) -fPIC

5 类型转换

1) ANSI C规定,void指针可以复制给其他任意类型的指针,其他任意类型的指针也可以复制给void指针,他们之间复制不需要强制类型转换。但是c++不支持,需要做强制类型转换。

buffer = palloc(MAX_LINESIZE);  ->  buffer = (char*)palloc(MAX_LINESIZE);

2) 部分c++编译器不支持const char*到char*的隐式转换,需要做强制类型转换。

6 函数声明

1) C语言中并没有重载和类这些特性,编译出的符号与C++不同,例如print(int i),不会被编译为_print_int,而是直接编译为_print等。因此如果直接在C++中调用C的函数会失败,例如调用print(3),c++中实际上会去找_print_int(3),这样就会找不到。加上extern “C”,指示编译器这部分代码按C语言来进行编译,而不是C++。

extern PGDLLEXPORT Datum  orafce_to_char_timestamp(PG_FUNCTION_ARGS);  -> 

extern  "C" PGDLLEXPORT Datum  orafce_to_char_timestamp(PG_FUNCTION_ARGS);

可以通过nm -D so文件查看生成的符号。

7 安全函数整改

1) 推荐使用安全函数(可见securec.h),并对安全函数的返回值作检查,openGauss定义了几个常用的检查宏,如下。

#define check_memcpy_s(r)  securec_check_c((r), "", "")

#define check_memmove_s(r)  securec_check_c((r), "", "") 

#define check_memset_s(r)  securec_check_c((r), "", "") 

#define check_strcpy_s(r)  securec_check_c((r), "", "") 

#define check_strncpy_s(r)  securec_check_c((r), "", "") 

#define check_strcat_s(r)  securec_check_c((r), "", "") 

#define check_strncat_s(r)  securec_check_c((r), "", "") 

#define check_gets_s(r)  securec_check_ss_c((r), "", "") 

#define check_sprintf_s(r)  securec_check_ss_c((r), "", "") 

#define check_snprintf_s(r)  securec_check_ss_c((r), "", "") 

#define check_scanf_s(r)  securec_check_ss_c((r), "", "")

下面是安全函数整改的示例。

memcpy(d, u, clen);  ->  check_memcpy_s(memcpy_s(d, strlen(d), u, clen));

为了方便和完全地作安全函数整改,这里提供一个查找危险函数的正则表达式。

(wmemcpy\()|(wmemove\()|(memmove\()|(wcscpy\()|(wcsncpy\()|(strcat\()|(wcscat\()|(strncat\()|(wcsncat\()|(strtok\()|(wcstok\()|(sprintf\()|(swprintf\()|(vsprintf\()|(vswprintf\()|(snprintf\()|(vsnprintf\()|(vsnprintf_truncated\()|(snprintf_truncated\()|(scanf\()|(wscanf\()|(vscanf\()|(vwscanf\()|(fscanf\()|(fwscanf\()|(vfscanf\()|(vfwscanf\()|(sscanf\()|(swscanf\()|(vsscanf\()|(vswscanf\()|(gets\()|(strcpy\()|(strcpy\()|(strncpy\()|(strncpy\()|(strcat\()|(strncat\()|(memcpy\()|(memcpy\()|(memset\()|(memset\()

8 变量转换

1) 对比PostgreSQL,openGauss收集了原有的全局变量,将其收集在了g_instance、t_thrd、u_sess(分别是全局变量、线程变量和会话变量)等结构体内,因此需要作相应替换(通过编译报错体现,需要到内核代码层面查看变量具体存放位置)。插件的全局变量可通过nm -D so | grep ‘B’排查。(具体见7.7)

econtext = error_context_stack;  ->  econtext = t_thrd.log_cxt.error_context_stack;

2) PG采用进程模型,用户会话进来时会创建一个独立的进程去处理,此时插件定义的全局变量在该进程内就是唯一的会话变量。而openGauss采用线程模型,所有会话共享同一份全局变量,因此需要将全局变量修改为会话变量。对于只读的全局变量,保持原样即可,而对于多次修改的变量,需要作如下修改。

a. 如果不考虑在线程池模式下使用插件,将全局变量修改为THR_LOCAL变量,即线程变量,因为用户会话进来会创建一个独立的线程。

b. 如果需要线程池,就需要作额外的修改。线程池模式下,一个用户会话可能会切换多个线程,单纯的将全局变量改为线程变量,在切换线程时会丢失对该变量的修改。openGauss提供了插件自定义会话变量的方式,具体实现如下。(以dblink为例)

内核侧在u_sess中定义一个指针数组extension_session_vars_array

,和标识数组大小的变量extension_session_vars_array_size

,数组用于存放插件会话变量的结构体。

typedefstructknl_session_attr_common{

uint32 extension_session_vars_array_size;

void** extension_session_vars_array;

} knl_session_attr_common;

插件侧需定义一个全局的下标变量,用于获取数组元素,并且提供set_extension_index

函数,内核侧会调用该函数来设置下标。示例如下。

staticuint32 dblink_index;

voidset_extension_index(uint32 index){

dblink_index = index; 

}

此外,插件侧还需要定义步骤1提到的会话变量结构体,存放该插件自身所有的会话变量,以及提供函数init_session_vars,主要是初始化该结构体,并把指针存放在数组的对应下标位置。示例如下。

#include"commands/extension.h"

typedefstructdblink_session_context{

remoteConn* pconn;

HTAB* remoteConnHash;

} dblink_session_context;   

voidinit_session_vars(void)

{     

RepallocSessionVarsArrayIfNecessary();

dblink_session_context* psc = (dblink_session_context*)MemoryContextAllocZero(u_sess->self_mem_cxt,sizeof(dblink_session_context));

u_sess->attr.attr_common.extension_session_vars_array[dblink_index] = psc;     

psc->pconn =NULL;

psc->remoteConnHash =NULL;

}

最终,在插件使用会话变量时,根据下标到数组中获取对应的结构体指针即可。

dblink_session_context*get_session_context()

{   

if(u_sess->attr.attr_common.extension_session_vars_array[dblink_index] ==NULL) {

    init_session_vars();   

}   

return(dblink_session_context*)u_sess->attr.attr_common.extension_session_vars_array[dblink_index];

}   

voidexample()

{   

remoteConn* pconn = get_session_context()->pconn; 

}

具体方案实现可见社区PR(https://gitee.com/opengauss/openGauss-server/pulls/1101),插件整改可参考其中对dblink的整改。

9 其他

除了上述修改点,还存在很多一些较为细节的地方,其中包括有C和C++的差异,例如在C++中new关键字不能作标识符等;大多数还是openGauss和PostgreSQL内核上的差异,下文会对这些差异作详细说明。此外,有些插件可能是基于PG内核新特性开发的,openGauss并不支持,可以考虑将特性整合到插件,必要时修改内核。

下面列举openGauss和PostgreSQL(REL_13_STABLE)内核上的差异,第2章中提到该部分需要不断更新完善,目前仅列出极少部分。

9.1 API

序号API_01

PostgreSQLvoid table_close(Relation relation, LOCKMODE lockmode);

openGauss#define heap_close(r,l) relation_close(r,l) void relation_close(Relation relation, LOCKMODE lockmode);

作用close any relation

差异名称不同

序号API_02

PostgreSQLRelation table_open(Oid relationId, LOCKMODE lockmode)

openGaussRelation heap_open(Oid relationId, LOCKMODE lockmode, int2 bucketid=-1);

作用open a heap relation by relation OID

差异名称不同;openGauss的heap_open增加了一个可选参数bucketid

9.2 系统表

序号SYSTAB_01

系统表pg_class

差异openGauss新增字段:reltoastidxid, reldeltarelid, reldeltaidx, relcudescrelid, relcudescidx, relhasoids, relhaspkey, relcmprs, relhasclusterkey, relrowmovement, parttype, relfrozenxid64, relbucket, relbucketkey

PostgreSQL 新增字段:relrowsecurity, relforcerowsecurity, relispopulated, relispartition, relrewrite , relminmxid , relpartbound relkind

字段可选值差异:PostgreSQL中用p和I表示分区表和分区索引,openGauss用字段parttype表示。

备注具体描述可见《开发者指南》-系统表和系统视图-系统表-PG_CLASS

9.3 系统视图

序号SYSVIEW_01

系统表pg_tables

差异openGauss新增字段:tablecreator, created, last_ddl_time PostgreSQL 新增字段:rowsecurity

备注具体描述可见《开发者指南》-系统表和系统视图-系统视图-PG_TABLES

9.4 系统函数

9.5 LOCK

9.6 Memory Context

9.7 全局变量

PostgreSQLopenGauss作用域

error_context_stackt_thrd.log_cxt.error_context_stackThread

WalSndCaughtUpt_thrd.walsender_cxt.walSndCaughtUpThread

disable_costg_instance.cost_cxt.disable_costInstance

cpu_tuple_costu_sess->attr.attr_sql.cpu_tuple_costcpu_tuple_cost

10 常见错误信息

1) 编译安装时报错:dangerous relocation: unsupported relocation

解决方法:参考4.2,在Makefile中添加下面一句。

overrideCPPFLAGS :=$(filter-out -fPIE,$(CPPFLAGS))-fPIC

2) 编译安装时报错:error: invalid conversion from ‘void’ to ‘char’ [-fpermissive]

解决方法:参考5类型转换

3) create extension时报错:could not find function “xxx” in file “xxx.so”

解决方法:参考6函数声明。

更多内容请参考:

https://docs.opengauss.org/zh/docs/3.1.0/docs/BriefTutorial/BriefTutorial.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容