Работа с PostgreSQL на C при помощи библиотеки libpq

16 января 2017

Нет причин не продолжить наше с вами изучение библиотек для языка C. Ранее в этом блоге рассматривались библиотеки libcurl, libpcap, а также некоторые сильно менее распространенные. Сегодня же мы узнаем, как программы на C могут работать с реляционными базами данных.

Перейдем сразу к коду:

#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

// (c) Aleksander Alekseev 2016 | http://eax.me/

#define UNUSED(x) (void)(x)

static const char* user_phone_arr[][2] = {
    { "user111", "phone111" },
    { "user222", "phone222" },
    { "user333", "phone333" },
    { NULL, NULL }
};

static PGconn* conn = NULL;
static PGresult* res = NULL;

static void
terminate(int code)
{
    if(code != 0)
        fprintf(stderr, "%s\n", PQerrorMessage(conn));

    if(res != NULL)
        PQclear(res);

    if(conn != NULL)
        PQfinish(conn);

    exit(code);
}

static void
clearRes()
{
    PQclear(res);
    res = NULL;
}

static void
processNotice(void *arg, const char *message)
{
    UNUSED(arg);
    UNUSED(message);

    // do nothing
}

int
main()
{
    int libpq_ver = PQlibVersion();
    printf("Version of libpq: %d\n", libpq_ver);

    conn = PQconnectdb("user=eax password= host=127.0.0.1 dbname=eax");
    if(PQstatus(conn) != CONNECTION_OK)
        terminate(1);

    // Don't output notices like:
    // NOTICE:  relation "phonebook" already exists, skipping
    // see http://stackoverflow.com/a/12504406/1565238
    PQsetNoticeProcessor(conn, processNotice, NULL);

    int server_ver = PQserverVersion(conn);
    char *user = PQuser(conn);
    char *db_name = PQdb(conn);

    printf("Server version: %d\n", server_ver);
    printf("User: %s\n", user);
    printf("Database name: %s\n", db_name);

    // same for insert, update, delete, begin, commit ...
    res = PQexec(conn, "CREATE TABLE IF NOT EXISTS phonebook "
            "(id SERIAL PRIMARY KEY, name VARCHAR(64), "
            "phone VARCHAR(64), last_changed TIMESTAMP)");
    if(PQresultStatus(res) != PGRES_COMMAND_OK)
        terminate(1);
    clearRes();

    res = PQexec(conn, "DELETE FROM phonebook");
    if(PQresultStatus(res) != PGRES_COMMAND_OK)
        terminate(1);
    clearRes();

    const char* query =
        "INSERT INTO phonebook (name, phone, last_changed) "
        " VALUES ($1, $2, now());";
    const char* params[2];

    for(int i = 0; ; i++)
    {
        const char* user = user_phone_arr[i][0];
        const char* phone = user_phone_arr[i][1];
        if(user == NULL || phone == NULL)
            break;

        params[0] = user;
        params[1] = phone;

        res = PQexecParams(conn, query, 2, NULL, params,
                           NULL, NULL, 0);
        if(PQresultStatus(res) != PGRES_COMMAND_OK)
            terminate(1);
        clearRes();
    }

    res = PQexec(conn, "SELECT id, name, phone, last_changed "
                       "FROM phonebook");
    if(PQresultStatus(res) != PGRES_TUPLES_OK)
        terminate(1);

    int ncols = PQnfields(res);
    printf("There are %d columns:", ncols);
    for(int i = 0; i < ncols; i++)
    {
        char *name = PQfname(res, i);
        printf(" %s", name);
    }
    printf("\n");

    int nrows = PQntuples(res);
    for(int i = 0; i < nrows; i++)
    {
        char* id = PQgetvalue(res, i, 0);
        char* name = PQgetvalue(res, i, 1);
        char* phone = PQgetvalue(res, i, 2);
        char* last_changed = PQgetvalue(res, i, 3);
        printf("Id: %s, Name: %s, Phone: %s, Last changed: %s\n",
            id, name, phone, last_changed);
    }

    printf("Total: %d rows\n", nrows);

    clearRes();
    terminate(0);
    return 0;
}

Как видите, тут приводится пример создания и удаления таблиц, запись в базу и чтение из нее — практически все, что вам когда-либо может понадобится. Мне кажется, что код достаточно прост, чтобы я мог его не разжевывать. Все подробности в случае необходимости вы найдете в официальной документации.

GitHub-репозиторий с приведенным выше кодом находится здесь. Как обычно, если что-то не понятно, или вам есть, что добавить к написанному, не бойтесь использовать комментарии.

Дополнение: Внутренности PostgreSQL: сетевой протокол

Метки: , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.