1

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:

  1. Compile my library (warp_client.c) with flag -fPIC into an object file.

gcc -c -fPIC warp_client.c -o static/warp_client.o

  1. Create static library from the object file.

ar -rcs out/libwarp_client.a static/warp_client.o

  1. Copy libwarp_client.a and warp_client.h into the postgresql extension project root.
  2. 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

  1. 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

Hyrikan
  • 59
  • 7

1 Answers1

0

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 the SHLIB_LINK variable:

SHLIB_LINK = -lwarp

(That example assumes a library called libwarp.so.)

Laurenz Albe
  • 209,280
  • 17
  • 206
  • 263
  • I edited my question with the files in question. I only exported the handler and validator functions needed for the FDW to work. I don't plan to use the functions from warp_client.c out of postgresql, only inside of the FDW. Do i still need to export them? – Hyrikan Jun 03 '22 at 19:51
  • No, I think I misunderstood your problem. See my fixed answer. – Laurenz Albe Jun 04 '22 at 20:25
  • Your suggestion was completely right, i got a step forward to the solution, thanks! I explained my next steps in edit 2. Now, postgres fails to find a shared library used in my own shared library. Shlibception, it seems. Do i also need to add shared libraries used in my own into the Makefile, even though i used them in the compile process of my own shared library? – Hyrikan Jun 09 '22 at 10:15
  • I did not reflect my change from a static libwarp_client.a library (which also uses that shared UCP library) to a shared libwarp_client.so library. Its hard to include it all in the question :). Should i include this change and the way i compiled the shared library? SHLIB_LINK = -lwarp_client only works with shared libs, so i had to change :) – Hyrikan Jun 09 '22 at 10:44
  • Is `libwarp_client.so` linked with `libucp.so.0`? Check with `ldd`. It should be, but if it is not, perhaps you can link with that library too. – Laurenz Albe Jun 09 '22 at 11:31
  • Output of `ldd libwarp_client.so` is: `linux-vdso.so.1 (0x00007ffddded5000)` `libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4412308000)` `/lib64/ld-linux-x86-64.so.2 (0x00007f441253e000)` I will look into, why it is not linked with UCP. – Hyrikan Jun 13 '22 at 08:08
  • OK the extension works! I had to include -lucp into the Makefile. I did not fully get my head around the dependencies of shared libraries, but it works for now and thats fine. Ideally i would like to use my shared library without the need to include the dependency of my own shared library. UCP gets only used in my library and not in the fdw. – Hyrikan Jun 13 '22 at 10:33
  • Then you have to build your library properly, so that it is linked with the UCP library. Then you don't need to link the FDW shared object with the UCP library.. – Laurenz Albe Jun 13 '22 at 14:33