什么是链接Postgres服务器端C函数的适当库

我正在尝试使用Ubuntu 14.04在Postgres 9.5的平台上编译一些C扩展。

在我的例子中,我想编写我的C代码并首先将其编译为独立的可执行文件(如下面的Makefile中所示)。 这是因为我还使用NumPy API并编写将Postgres ArrayType数组转换为NumPy PyArray对象的函数,然后使用一些NumPy数组函数。 获取正确的细节,正确释放NpyIter对象等等是非常棘手的,所以我绝对需要编译,运行,观察错误和测试所有内容, 然后在最终部分中为最终部分构建库的详细信息,我说CREATE EXTENSION Postgres中的CREATE EXTENSION

编译时,我得到了几个未定义的引用问题,例如:

 tmp.c:(.text+0x2d6): undefined reference to `get_typlenbyvalalign' tmp.c:(.text+0x346): undefined reference to `deconstruct_array' tmp.c:(.text+0x41f): undefined reference to `DatumGetFloat8' tmp.c:(.text+0x4ae): undefined reference to `pfree' tmp.c:(.text+0x4ba): undefined reference to `pfree' 

这些是来自Postgres C API的服务器端函数,但是有很多谷歌搜索和许多pgxs我无法弄清楚如何获取我无法链接的后端Postgres库的名称或路径。

几乎所有搜索都提到了libpq ,但是这些函数没有在客户端API库中定义,所以我正在寻找其他东西。

作为参考,这是我目前正在使用的Makefile。 包括来自pg_config --libdir的库目录也必须是不正确的,因为它不会导致未定义的引用错误的任何更改。

 INCLUDEDIRS := -I. INCLUDEDIRS += -I/usr/include/python2.7 INCLUDEDIRS += -I/home/username/anaconda/lib/python2.7/site-packages/numpy/core/include INCLUDEDIRS += -I$(shell pg_config --includedir-server) INCLUDEDIRS += -I$(shell pg_config --includedir) LIBS := -L$(shell pg_config --libdir) LIBS += -lpython2.7 tmp: tmp.c Makefile gcc tmp.c -o tmp $(INCLUDEDIRS) $(LIBS) 

输出pg_config --libdir是:

 user@computer:~/programming$ pg_config --libdir /usr/lib/x86_64-linux-gnu 

在那个库目录中我也发现了libpgcommon ,当我将它添加到Makefile时,一些未定义的引用会消失,但不是全部。 这些仍然存在:

 tmp.c:(.text+0x2d6): undefined reference to `get_typlenbyvalalign' tmp.c:(.text+0x346): undefined reference to `deconstruct_array' tmp.c:(.text+0x41f): undefined reference to `DatumGetFloat8' 

所以通过链接pfree找到了libpgcommon ,但没有别的。

postgres.h进一步挖掘我可以看到DatumGetFloat8宏的定义位置(第662行):

 #ifdef USE_FLOAT8_BYVAL extern float8 DatumGetFloat8(Datum X); #else #define DatumGetFloat8(X) (* ((float8 *) DatumGetPointer(X))) #endif 

因此,Postgres必须以某种方式安装,使用USE_FLOAT8_BYVAL标志。 那是标准吗? 您是否期望从普通的Postgres安装使用包Repos来安装像Ubuntu这样的流行操作系统?

鉴于此, DatumGetFloat8extern源代码或库是什么? 例如,在Google上搜索“postgres DatumGetFloat8”几乎没有说明这一点。 我能找到的最好的是来自2011年的消息线程 (不确定是否正确):

缺少对DatumGetFloat8的引用意味着服务器已构建
使用float8传递值,并使用float8传递来构建pljava
参考。

pljava位对我来说无关紧要)。

PostgreSQL扩展是一个共享库,而不是一个独立的可执行文件,所以你必须在Makefile中使用-shared -fpic -o tmp.so 不要与-lpq-lpgcommon链接。

然后gcc不会抱怨未定义的引用 – 当共享库加载到PostgreSQL中时,使用SQL命令LOAD或者调用使用该库定义的PostgreSQL C函数时,将使用可执行文件postgres解析这些引用。

当然,在构建PostgreSQL服务器时,你必须使用相同的-D USE_FLOAT8_BYVAL作为定义,以便例如在两者中设置或取消设置USE_FLOAT8_BYVAL 。 否则,您的扩展程序可能无法加载或以有趣的方式崩溃。
(因为你问:按值传递float8没有什么不好;如果你的架构支持它,它实际上更有效。如果定义了USE_FLOAT8_BYVAL则在postgres定义和导出USE_FLOAT8_BYVAL 。)

为了使这一切变得简单,最好使用PostgreSQL扩展构建基础结构PGXS 。 这将为您解决所有这些问题。

您所要做的就是创建一个合适的Makefile ,在您的情况下它可能就像创建一样简单

 MODULES = tmp PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) 

然后运行makemake install ,你就可以开始了!

由于您的问题是测试和调试使用NumPy API的代码,因此您应该模块化PostgreSQL扩展,以便您可以更轻松地执行此操作。 例如,您可以编写numpy.c ,其中包含访问该API的所有代码,以及包含PostgreSQL扩展代码并在numpy.c调用函数的numpy.c
然后,您可以编写特殊的测试代码并将其与numpy.o链接以测试您的NumPy函数。

如果你正在写Postgres 扩展 (你说CREATE EXTENSION foo的那种),你不需要太担心所有INCLUDEDIR等东西。 它比你想象的容易! 我写了几个非常简单的Postgres扩展,尽管每年只做几次C,但实际上它很容易。 你的Makefile可以是6行长,如下所示:

 MODULES = myextensionname EXTENSION = myextensionname DATA = myextensionname--1.0.sql PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) 

您还应该有一个myextensionname.control文件,如下所示:

 comment = 'Something about your extension' default_version = '1.0' module_pathname = '$libdir/myextensionname' relocatable = true 

你需要一个myextensionname.c如下所示:

 #include  #include  #include  #include  // whatever other things you need to include here PG_MODULE_MAGIC; // your code here 

然后编写一个myextensionname--1.0.sql文件,当有人说CREATE EXTENSION时运行myextensionname--1.0.sql文件,这就是你创建类型/函数/你需要的任何其他东西(在SQL中)。

如果你想要一些非常容易复制的例子,这是我的两个项目:

https://github.com/pjungwir/aggs_for_arrays

https://github.com/pjungwir/inetrange

如果您想要更高级的扩展示例,请查看本地Postgres结帐的contrib目录。

关于你的具体问题:

因此,Postgres必须以某种方式安装,使用USE_FLOAT8_BYVAL标志。

如果您使用类似上面的Makefile ,则无需担心,因为您的扩展将使用相同的标志进行编译,因此它将以与安装的Postgres相同的方式处理DatumGetFloat8 (无论是作为函数还是宏)。 我想你会发现USE_FLOAT_BYVAL只是冰山一角,所以你应该使用include $(PGXS)来匹配所有东西,而不是设置单独的标志。

编辑:我刚刚看到你的评论,你想编写一个独立的二进制文件用于测试目的。 我认为在进行扩展我会这样做,这样你就可以更熟悉这个过程了。 此外,您可以查看我自己的项目和contrib扩展,以获取其他人如何处理测试的示例。