使用自定义库安装PostgreSQL扩展的正确方法
tl; dr
必须以共享库来编译其自定义库:
gcc -c -fPIC warp_client.c -o warp_client.o
gcc -shared warp_client.o libwarp-client.so
包括共享库和该共享库的其他依赖项在PostgreSql makefile中使用标志shlib_link和pg_ldflags(这里是bachelor_fdw.c,扩展到编译):
EXTENSION = bachelor_fdw
MODULE_big = bachelor_fdw
DATA = bachelor_fdw--0.1.sql
OBJS = bachelor_fdw.o
PG_LIBS = -lpq
SHLIB_LINK = -lwarp_client -lucp
PG_LDFLAGS += -L/usr/lib/warpdrive/ -L/usr/lib/ucx/
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
将共享库的目录包括到postgresql的环境变量ld_library_path中。为此,必须在主要的PostgreSQL目录中的文件“环境”和RISTART POSTGRESQL中添加一行。这是我的:
$ cat /etc/postgresql/12/main/environment
# environment variables for postgres processes
# This file has the same syntax as postgresql.conf:
# VARIABLE = simple_value
# VARIABLE2 = 'any value!'
# I. e. you need to enclose any value which does not only consist of letters,
# numbers, and '-', '_', '.' in single quotes. Shell commands are not
# evaluated.
LD_LIBRARY_PATH='/usr/include/:/usr/include/ucx/:/usr/lib/:/usr/lib/ucx/'
我正在尝试创建一个外国数据包装器,它使用我的自定义库。 FDW编译并安装正常,但是当使用它时,我库的符号是未定义的。在PostgreSQL扩展程序中使用自定义C代码作为库的正确方法是什么?我在做什么错?这是我采取的步骤:
- 将我的库(warp_client.c)和flag -fpic编译到对象文件中。
gcc -c -c -fpic warp_client.c -o static/warp_client.o
- 从对象文件创建静态库。
ar -rcs out/libwarp_client.a static/warp_client.o
- 复制libwarp_client.a and arawp_client.h coplypostgresql扩展项目root。
- 与以下makefile一起编译PostgreSQL扩展。
EXTENSION = bachelor_fdw
MODULE_big = bachelor_fdw
DATA = bachelor_fdw--0.1.sql libwarp_client.a
OBJS = bachelor_fdw.o
HEADERS = warp_client.h
ifdef DEBUG
$(info $(shell echo "debug ist an"))
endif
PG_LIBS = -lpq
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
make use_pgxs = 1安装
- 尝试创建扩展名。该扩展程序在_pg_ini()函数中调用库函数。出现错误:
创建扩展名(如果不存在)bachelor_fdw;
psql:only_create.sql:3: ERROR: could not load library "/usr/lib/postgresql/12/lib/bachelor_fdw.so": /usr/lib/postgresql/12/lib/bachelor_fdw.so: undefined symbol: warpclient_getData
warp_client.h具有功能标头,warp_client.c具有功能。 warp_client.c包括“ warp_client.h”,bachelor_fdw.c(扩展名)包括“ warp_client.h”。
warp_client.h:
#ifndef TEST_FIELD_UCP_WARP_CLIENT_H
#define TEST_FIELD_UCP_WARP_CLIENT_H
#include <ucp/api/ucp.h>
int warpclient_queryServer(char *server_addr_local, int port, int useINet6, char *query);
void *warpclient_getData();
int warpclient_cleanup();
#endif //TEST_FIELD_UCP_WARP_CLIENT_H
还有更多所需的信息吗?我真的很高兴提供任何帮助。
编辑1
我在bachelor_fdw.c的内部使用warp_client.h的功能。我还需要导出它们吗?我认为只有从PostgreSQL Server被调用的功能需要导出。
这是bachelor_fdw.c的一部分:
#include <warp_client.h>
#include "postgres.h"
#include "foreign/fdwapi.h"
#include "foreign/foreign.h"
#include "nodes/nodes.h"
#include "optimizer/pathnode.h"
#include "optimizer/planmain.h"
...
PG_MODULE_MAGIC;
/*
* SQL functions
*/
PG_FUNCTION_INFO_V1(bachelor_fdw_handler);
PG_FUNCTION_INFO_V1(bachelor_fdw_validator);
/*
* Extension initialization functions
*/
extern void _PG_init(void);
extern void _PG_fini(void);
/*
* FDW callback routines
*/
static void bachelorBeginForeignScan(ForeignScanState *node, int eflags);
static TupleTableSlot *bachelorIterateForeignScan(ForeignScanState *node);
static void bachelorReScanForeignScan(ForeignScanState *node);
static void bachelorEndForeignScan(ForeignScanState *node);
static void bachelorGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
static void bachelorGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
static ForeignScan* bachelorGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan);
void _PG_init(void){
int ret = 0;
void *data;
ret = warpclient_queryServer(NULL, -1, 0, "SELECT TEST FROM TEST;");
elog_debug("Testquery for server. Return code (%d)...\n", ret);
while(NULL != (data = warpclient_getData())){
elog_debug("Data received as fdw: %s\n", data);
}
elog_debug("Finished receiving data.\n");
/* Call cleanup */
ret = warpclient_cleanup();
elog_debug("Warpclient cleanup (%d)...\n", ret);
}
这是warp_client.c的一部分:
#include "warp_client.h"
...
int warpclient_cleanup(){
int ret = 0;
//free buffers
free(recvbuffer->buffer);
free(recvbuffer);
/* Close the endpoint to the server */
debugmsg("Close endpoint.\n");
ep_close();
/* releasing UCX ressources */
ucp_worker_destroy(ucp_worker);
ucp_cleanup(ucp_context);
return ret;
}
int warpclient_queryServer(char *server_addr_local, int port, int useINet6, char *query){
/*
* Initialize important connection variables
*/
debugmsg("Initializing connection variables...\n");
if(NULL != server_addr_local) server_addr = server_addr_local;
if((port >= 0) && (port <= UINT16_MAX)) server_port = port;
if(useINet6) ai_family = AF_INET6;
int ret;
/* Initialize the UCX required objects worker and context*/
debugmsg("Initializing context and worker...\n");
ret = init_context_and_worker();
if (ret != 0) {
fprintf(stderr, "Initializing worker or context failed! Exiting..\n");
return -2;
}
/*
* UCP objects: client_ep as communication endpoint for the worker.
* status for function error code check.
*/
ucs_status_t status;
/* ep initialization and exchange with server over sockets */
debugmsg("Creating Client endpoint.\n");
status = create_client_endpoint();
if (status != UCS_OK) {
fprintf(stderr, "failed to start client (%s)\n", ucs_status_string(status));
return -1;
}
ret = send_query(query);
if(ret!=0){
debugmsg("Failed to connect to Server.\n");
}
return ret;
}
编辑2
我成功地向前迈出了一步。但是我仍然对共享库中使用的共享库有问题。我是否还需要链接到我自己的共享库中使用的共享库,即使我在分发之前对共享库进行了编译时将其链接起来?
我做的是:
我添加了shlib_link = -lwarp_client
在makefile中,还需要行pg_ldflags += -l。 我还设法为Postgres服务包含环境变量LD_Library_path,以便可以在标准位置找到我的库。并从makefile中的数据标志中删除了库。
新的makefile:
EXTENSION = bachelor_fdw
MODULE_big = bachelor_fdw
DATA = bachelor_fdw--0.1.sql
OBJS = bachelor_fdw.o
ifdef DEBUG
$(info $(shell echo "debug ist an"))
endif
PG_LIBS = -lpq
SHLIB_LINK = -lwarp_client
PG_LDFLAGS += -L.
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
求婚变量:
/proc/1551/environ | xargs -0 -n 1 echo
LD_LIBRARY_PATH=/usr/include/:/usr/include/ucx/:/usr/lib/:/usr/lib/ucx/
...
在扩展程序上使用创建时,我的库会使用,但Postgres抱怨我的库使用了另一个共享库。
psql:only_create.sql:3: ERROR: could not load library "/usr/lib/postgresql/12/lib/bachelor_fdw.so": /usr/lib/warpdrive/libwarp_client.so: undefined symbol: ucp_ep_create
错误清楚地说,它使用了我的共享库中的共享库中,其中包括标准目录中的子目录“ warpdrive”。来自UCP的共享库也在该标准目录中:
ls /usr/lib/ucx
cmake libjucx.so.0.0.0 libucp.a libucs.la libuct.so
jucx-1.12.1.jar libucm.a libucp.la libucs.so libuct.so.0
libjucx.a libucm.la libucp.so libucs.so.0 libuct.so.0.0.0
libjucx.la libucm.so libucp.so.0 libucs.so.0.0.0 pkgconfig
libjucx.so libucm.so.0 libucp.so.0.0.0 libuct.a ucx
libjucx.so.0 libucm.so.0.0.0 libucs.a libuct.la
TL;DR
One has to compile their custom library as shared library:
gcc -c -fPIC warp_client.c -o warp_client.o
gcc -shared warp_client.o libwarp-client.so
Include the shared library and additional dependencies of that shared library in the Postgresql Makefile with the flags SHLIB_LINK and PG_LDFLAGS(Here the bachelor_fdw.c is the extension to compile):
EXTENSION = bachelor_fdw
MODULE_big = bachelor_fdw
DATA = bachelor_fdw--0.1.sql
OBJS = bachelor_fdw.o
PG_LIBS = -lpq
SHLIB_LINK = -lwarp_client -lucp
PG_LDFLAGS += -L/usr/lib/warpdrive/ -L/usr/lib/ucx/
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
Include the directories of the shared libraries into the environment variable LD_LIBRARY_PATH of Postgresql. For that, one has to add a line to the file 'environment' in the main Postgresql directory and restart Postgresql. Here is mine:
$ cat /etc/postgresql/12/main/environment
# environment variables for postgres processes
# This file has the same syntax as postgresql.conf:
# VARIABLE = simple_value
# VARIABLE2 = 'any value!'
# I. e. you need to enclose any value which does not only consist of letters,
# numbers, and '-', '_', '.' in single quotes. Shell commands are not
# evaluated.
LD_LIBRARY_PATH='/usr/include/:/usr/include/ucx/:/usr/lib/:/usr/lib/ucx/'
I am trying to create a foreign data wrapper, which uses a custom library from me. The fdw compiles and installs fine, but when using it, symbols to my library are undefined. What is the proper way of using custom c code as library in a postgresql extension and what am i doing wrong? Here are the steps i took:
- Compile my library (warp_client.c) with flag -fPIC into an object file.
gcc -c -fPIC warp_client.c -o static/warp_client.o
- Create static library from the object file.
ar -rcs out/libwarp_client.a static/warp_client.o
- Copy libwarp_client.a and warp_client.h into the postgresql extension project root.
- Compile postgresql extension with the following makefile.
EXTENSION = bachelor_fdw
MODULE_big = bachelor_fdw
DATA = bachelor_fdw--0.1.sql libwarp_client.a
OBJS = bachelor_fdw.o
HEADERS = warp_client.h
ifdef DEBUG
$(info $(shell echo "debug ist an"))
endif
PG_LIBS = -lpq
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
make USE_PGXS=1 install
- Try to create the extension. The extension makes a call to a library function in it's _PG_INI() function. Error comes up:
CREATE EXTENSION IF NOT EXISTS bachelor_fdw;
psql:only_create.sql:3: ERROR: could not load library "/usr/lib/postgresql/12/lib/bachelor_fdw.so": /usr/lib/postgresql/12/lib/bachelor_fdw.so: undefined symbol: warpclient_getData
The warp_client.h has the function headers and warp_client.c has the functions. warp_client.c includes "warp_client.h", bachelor_fdw.c (the extension) includes "warp_client.h".
warp_client.h:
#ifndef TEST_FIELD_UCP_WARP_CLIENT_H
#define TEST_FIELD_UCP_WARP_CLIENT_H
#include <ucp/api/ucp.h>
int warpclient_queryServer(char *server_addr_local, int port, int useINet6, char *query);
void *warpclient_getData();
int warpclient_cleanup();
#endif //TEST_FIELD_UCP_WARP_CLIENT_H
Any more desired info? I would be really glad for any help.
EDIT 1
I use the functions from warp_client.h inside of bachelor_fdw.c. Do i still need to export them? I thought only functions, which get called from the postgresql server needs to be exported.
Here is part of bachelor_fdw.c:
#include <warp_client.h>
#include "postgres.h"
#include "foreign/fdwapi.h"
#include "foreign/foreign.h"
#include "nodes/nodes.h"
#include "optimizer/pathnode.h"
#include "optimizer/planmain.h"
...
PG_MODULE_MAGIC;
/*
* SQL functions
*/
PG_FUNCTION_INFO_V1(bachelor_fdw_handler);
PG_FUNCTION_INFO_V1(bachelor_fdw_validator);
/*
* Extension initialization functions
*/
extern void _PG_init(void);
extern void _PG_fini(void);
/*
* FDW callback routines
*/
static void bachelorBeginForeignScan(ForeignScanState *node, int eflags);
static TupleTableSlot *bachelorIterateForeignScan(ForeignScanState *node);
static void bachelorReScanForeignScan(ForeignScanState *node);
static void bachelorEndForeignScan(ForeignScanState *node);
static void bachelorGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
static void bachelorGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
static ForeignScan* bachelorGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan);
void _PG_init(void){
int ret = 0;
void *data;
ret = warpclient_queryServer(NULL, -1, 0, "SELECT TEST FROM TEST;");
elog_debug("Testquery for server. Return code (%d)...\n", ret);
while(NULL != (data = warpclient_getData())){
elog_debug("Data received as fdw: %s\n", data);
}
elog_debug("Finished receiving data.\n");
/* Call cleanup */
ret = warpclient_cleanup();
elog_debug("Warpclient cleanup (%d)...\n", ret);
}
And here is part of warp_client.c:
#include "warp_client.h"
...
int warpclient_cleanup(){
int ret = 0;
//free buffers
free(recvbuffer->buffer);
free(recvbuffer);
/* Close the endpoint to the server */
debugmsg("Close endpoint.\n");
ep_close();
/* releasing UCX ressources */
ucp_worker_destroy(ucp_worker);
ucp_cleanup(ucp_context);
return ret;
}
int warpclient_queryServer(char *server_addr_local, int port, int useINet6, char *query){
/*
* Initialize important connection variables
*/
debugmsg("Initializing connection variables...\n");
if(NULL != server_addr_local) server_addr = server_addr_local;
if((port >= 0) && (port <= UINT16_MAX)) server_port = port;
if(useINet6) ai_family = AF_INET6;
int ret;
/* Initialize the UCX required objects worker and context*/
debugmsg("Initializing context and worker...\n");
ret = init_context_and_worker();
if (ret != 0) {
fprintf(stderr, "Initializing worker or context failed! Exiting..\n");
return -2;
}
/*
* UCP objects: client_ep as communication endpoint for the worker.
* status for function error code check.
*/
ucs_status_t status;
/* ep initialization and exchange with server over sockets */
debugmsg("Creating Client endpoint.\n");
status = create_client_endpoint();
if (status != UCS_OK) {
fprintf(stderr, "failed to start client (%s)\n", ucs_status_string(status));
return -1;
}
ret = send_query(query);
if(ret!=0){
debugmsg("Failed to connect to Server.\n");
}
return ret;
}
EDIT 2
I managed to get a good step forward thanks to Laurenz Albe. But i still have a problem with a shared library used in my shared library. Do I also need to link to shared libraries used in my own shared library, even though i linked that as i compiled my shared library before distribution?
what I did:
I added SHLIB_LINK = -lwarp_client
to the Makefile and also needed the line PG_LDFLAGS += -L.
for the linker to find libwarp_client.so.
I also managed to include the environment variable LD_LIBRARY_PATH for the postgres service, so that it can find my library in the standard places. And removed the library from the DATA flag in the Makefile.
New Makefile:
EXTENSION = bachelor_fdw
MODULE_big = bachelor_fdw
DATA = bachelor_fdw--0.1.sql
OBJS = bachelor_fdw.o
ifdef DEBUG
$(info $(shell echo "debug ist an"))
endif
PG_LIBS = -lpq
SHLIB_LINK = -lwarp_client
PG_LDFLAGS += -L.
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
Enrivonment variables:
/proc/1551/environ | xargs -0 -n 1 echo
LD_LIBRARY_PATH=/usr/include/:/usr/include/ucx/:/usr/lib/:/usr/lib/ucx/
...
When using CREATE on the extension, my library gets used but postgres complains about another shared library, which my library uses.
psql:only_create.sql:3: ERROR: could not load library "/usr/lib/postgresql/12/lib/bachelor_fdw.so": /usr/lib/warpdrive/libwarp_client.so: undefined symbol: ucp_ep_create
The error clearly says, it uses my shared library from a subdirectory "warpdrive" in the included standard directory. The shared library from UCP is also in that standard directory:
ls /usr/lib/ucx
cmake libjucx.so.0.0.0 libucp.a libucs.la libuct.so
jucx-1.12.1.jar libucm.a libucp.la libucs.so libuct.so.0
libjucx.a libucm.la libucp.so libucs.so.0 libuct.so.0.0.0
libjucx.la libucm.so libucp.so.0 libucs.so.0.0.0 pkgconfig
libjucx.so libucm.so.0 libucp.so.0.0.0 libuct.a ucx
libjucx.so.0 libucm.so.0.0.0 libucs.a libuct.la
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
看起来像
warpclient_getData
在代码中使用,但是您没有将共享对象与提供该功能的库链接。将库添加到shlib_link
varible :(该示例假定一个名为
libwarp.so
的库。That looks like
warpclient_getData
gets used in your code, but you didn't link your shared object with the library that provides the function. Add the library to theSHLIB_LINK
variable:(That example assumes a library called
libwarp.so
.)