0

In the following ANSI C code, how could I convert the vector conns[] from fixed-size into dynamically allocated (i.e., perhaps by using malloc() and free() functions)?

#include <stdio.h>
#include <string.h>
#include "libpq-fe.h"

#define MAX_DATABASES 20

int main(int argc, char **argv)
{
    PGconn *conns[MAX_DATABASES]; // fixed-size vector
    int i, ndbs;

    ndbs = 3; // this value may vary

    memset(conns, 0, sizeof(conns));

    // instantiate connections
    for (i = 0; i < ndbs; i++) {
        conns[i] = PQconnectdb("dbname=template1");
    }

    // release connections
    for (i = 0; i < ndbs; i++) {
        fprintf(stdout, "%d) %p\n", i + 1, conns[i]);
        if (conns[i])
            PQfinish(conns[i]);
        conns[i] = NULL;
    }

    return 0;
}

The PGconn type is actually a typedef struct imported from /src/interfaces/libpq/libpq-fe.h:

typedef struct pg_conn PGconn;

The pg_conn is a struct found in /src/interfaces/libpq/libpq-int.h:

struct pg_conn
{
    char *pghost;
    char *pghostaddr;
    char *pgport;
    char *pgunixsocket;
    ...
};

The code above works successfully, despite being fixed-size. It can be compiled with the following instruction (PostgreSQL sources needed):

gcc -I/usr/src/postgresql-9.3/src/interfaces/libpq -I/usr/src/postgresql-9.3/src/include pqc.c -L/usr/src/postgresql-9.3/src/interfaces/libpq -lpq -lpthread -o pqc
Rodrigo Hjort
  • 301
  • 3
  • 5
  • You haven't tried anything yet right? What is this for? – Iharob Al Asimi Dec 24 '15 at 02:26
  • I've tried some `malloc()`ing, but was unsuccessful, mostly due to lack of experience in ANSI C. Unfortunately it seems there are no examples of LIBPQ handling multiple connections around there... :( – Rodrigo Hjort Dec 24 '15 at 02:50
  • I have a connection manager that picks connections when they are available (or a connection pool) if you preffer and uses multithreading to handle multiple connections. It was not hard to write, just basic knowledge of [tag:c] and *pthread*. Perhaps that is why there aren't many examples. If you like, I can send you the code. Just after the hollidays please, you can contact me here iharob@gmai.com. The contact information is on my profile too. – Iharob Al Asimi Dec 24 '15 at 02:53
  • Thanks, @iharob! I'd appreciate that code. Actually, yesterday I studied pthreads and developed a code using it. The only problem was that I used fixed-size vectors: [ https://github.com/hjort/pgstatmib/blob/master/labs/libpqpool/pqta.c ] – Rodrigo Hjort Dec 24 '15 at 02:58

2 Answers2

1

You don't have to change much, just use calloc:

PGconn** conns = calloc(MAX_DATABASES, sizeof(PGConn *));

and then remember to free(conns) in the end.

You don't need memset() as calloc() will already initialize the array with 0s.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
Jack
  • 131,802
  • 30
  • 241
  • 343
  • No `memset()` or `calloc()` is needed at all. Because there is no need to initialize the array to `0`'s. – Iharob Al Asimi Dec 24 '15 at 02:32
  • @iharob: it's better to get used to initialize things then caring about micro-optimization that can lead to subtle errors when you can't apply them. In this case it almost comes for free since you just need to use calloc instead that malloc. – Jack Dec 24 '15 at 02:35
  • It's not, because that might hide a bug in the code. Also, [read this](http://stackoverflow.com/questions/13894228/where-to-put-the-star-in-c-and-c-pointer-notation). Consider for instance `calloc()`ing or `memset()`ting a buffer that is meant for a string, your algorithm might have a bug and yet your program will behave perfectly. – Iharob Al Asimi Dec 24 '15 at 02:36
  • Simply using calloc() raised some errors when compiling: `pqc.c: In function ‘main’: pqc.c:34:30: error: invalid application of ‘sizeof’ to incomplete type ‘PGconn’ pqc.c:39:3: error: invalid use of undefined type ‘struct pg_conn’ pqc.c:39:8: error: dereferencing pointer to incomplete type` – Rodrigo Hjort Dec 24 '15 at 02:37
  • @iharob are you really argumenting about where I like to place the pointer specifier? That's non-sense. It's a matter of preference, an imposed standard doesn't exist. – Jack Dec 24 '15 at 02:38
  • @RodrigoHjort did you `#include `? – Iharob Al Asimi Dec 24 '15 at 02:38
  • It is a matter of preference that can make your code hard to read and understand. If you don't care about that I can also think that you don't care about code quality. And code quality affects (*not directly of course*) software quality. I always try to learn the best practices from those with more experience, like the person who answered the question at the link for example. – Iharob Al Asimi Dec 24 '15 at 02:39
  • No, you are talking about absolute visions. Code quality is not related on where the pointer specifier is placed. In my opinion (and probably also according to the other 50% of people who develop in C/C++) if you are declaring a type which a pointer then the information about it being a pointer is part of the type itself, so PGConn* makes more sense than PGConn. Your arguments are blatantly irrelevant. I would find it HARDER to read with pointer specifier attached to the variable name. Do you want to complain also on it? – Jack Dec 24 '15 at 02:41
  • @RodrigoHjort, no it has nothing to do with the includes. It's because `PGconn` is an opaque structure, just use this instead `conns = calloc(number_of_connections, sizeof(void *))`. – Iharob Al Asimi Dec 24 '15 at 02:41
  • @Jack No need to fight about that please. I think you exagerated 50% is a lot, I would think that the minority of [tag:c] or [tag:c++] programmers prefer that because it's far more common to see the star attached to the variable name. But please do not answer this. This is not what comments are for. I wanted to point out somethig and you are free to take the advice or not. And I didn't downvote because of that but because of the unnecessary initialization. – Iharob Al Asimi Dec 24 '15 at 02:43
  • I just fixed your post and removed the downvote. Now you see why did I downvote really! – Iharob Al Asimi Dec 24 '15 at 02:57
1

You can do it like this

PGconn **connections;
size_t number_of_connections;

number_of_connections = 10; // Do not exceed max_connections 
                            // from postgresql.conf 
                            // (default 100)
connections = malloc(number_of_connections * sizeof(*connections));
if (connections == NULL)
    return -1; // Allocation error, cannot continue
for (size_t i = 0 ; i < number_of_connections ; ++i)
    connections[i] = PQconnectdb("dbname=template1");
// Do whatever you want with connections, and free
for (size_t i = 0 ; i < number_of_connections ; ++i)
    PQfinish(connections[i]);
free(connections);

You don't need to set all the pointers to NULL, they will automatically be set if PQconnectdb() fails, so you can check that before trying to use the connection.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • The program raises a core dump due to `free(connections[i]);`. Isn't `free(connections);` enough to release the memory allocated in `malloc()`? – Rodrigo Hjort Dec 24 '15 at 02:42
  • It's not enough. And of course it does crash. Give me a second. Sorry, see the edit. I didn't notice that `connections[i]` was not assigned from a `malloc()`. Since you used `PQconnectdb()` to create the connections you must close the connections correctly with `PQfinish()` or you might leave the connections "*alive*" and then subsequent runs of the program will fail. – Iharob Al Asimi Dec 24 '15 at 02:45
  • BTW: now I see why the downvote. But it would have been better to comment in order to fix the answer instead of leaving the mistake there for future readers. If the OP didn't try the code I wouldn't have noticed the mistake. – Iharob Al Asimi Dec 24 '15 at 02:47