Using Postgres connection functions

Joe jma at freedomcircle.com
Sat Jan 13 04:17:02 UTC 2018


I'm trying to learn how to use D to connect (and send queries) to 
Postgres, i.e., libpq in C. Postgres has three families of 
connection functions: PQsetdbLogin which takes multiple 
individual arguments (all as const char *), PQconnectdb which 
takes a single connection string (which Postgres then has to 
parse into keywords/values) and PQconnectdbParams (introduced 
with PG 9.0 in 2010) which takes two arrays of char pointers, one 
for the keywords and another for the values, i.e., it's 
pre-parsed, and I believe, more convenient for callers since it's 
most likely that they get the parameter values from command line 
arguments, environment values or UI dialogs, so it saves the 
connection string formatting on the caller side and the 
parsing/decomposing on the library side.

In spite of the latter considerations, it appears most D 
libraries for Postgres only support connection string usage (only 
derelict-pq has a declaration for PQconnectdbParams--but I 
haven't tested it).

In any case, I tried to use PQconnectdbParams by first declaring 
it, as per the Postgres manual, with

extern(C)
{
     struct PGconn;

     PGconn *PQconnectdbParams(const char * const * keywords,
                               const char * const * values,
                               int expand_dbname);
}

This caused ldc2, on Linux, to complain as follows:

connection.d(30): Error: found 'const' when expecting ')'
connection.d(30): Error: semicolon expected following function 
declaration
connection.d(30): Error: declaration expected, not '*'

It only compiled after I removed the second 'const' in the first 
and second arguments.

The hard thing was figuring out how to pass the keyword/values 
arrays, defined as:

string[] keywords = ["hostaddr", "port", "dbname"];
string[] values = ["127.0.0.1", "5432", "testdb"];

to the D invocation of PQconnectdbParams.  I ended up with the 
following, which looks like covering a patient with lots of 
bandaids and looking the other way (and I had to modify the 
arrays above with a fixed length).

     extern(C) char * [keywords.length + 1] kws;
     extern(C) char * [keywords.length + 1] vals;
     foreach (i, k; keywords) {
         kws[i] = cast(char *)toStringz(k);
         vals[i] = cast(char *)toStringz(values[i]);
     }
     kws[keywords.length] = null;  // Postgres wants the NULL 
termination
     vals[keywords.length] = null;

     conn = PQconnectdbParams(cast(const char **)kws,
                              cast(const char **)vals, 1);

I assume that in a real program I'd have to malloc the C arrays, 
since I wouldn't know beforehand how many parameters there would 
be (or I could define them with a static size of about 30 since 
that's how many connection parameters are recognized currently).

So my question is: is there an easier or better way of passing 
two arrays of C null-terminated strings to an extern(C) function?



More information about the Digitalmars-d-learn mailing list