How to get the double value using libpq?

4.8k views Asked by At

The examples in the libpq documentation show how to get the the integer value by converting it to the host-endian representation.

I am curious what must be done to get the double precision value using libpq (without libpqtyppes)? I have tried reinterpret_cast with no luck.

Also why text and byte data doesn't need endian conversions?

The DB runs locally on Windows 7, I am using Visual C++ 2013.

pptr is a double vaule I am trying to retrieve.

#include <iostream>
#include <memory>
#include <vector>
#include <libpq-fe.h>
#include <Winsock2.h>


static void
show_binary_results(PGresult *res)
{
    int i, j;
    int i_fnum, n_fnum, p_fnum;

    /* Use PQfnumber to avoid assumptions about field order in result */
    i_fnum = PQfnumber(res, "id");
    n_fnum = PQfnumber(res, "name");
    p_fnum = PQfnumber(res, "price");

    for (i = 0; i < PQntuples(res); i++)
    {
        char* iptr;
        char* nptr;
        char* pptr;
        int         blen;
        int         ival;

        /* Get the field values (we ignore possibility they are null!) */
        iptr = PQgetvalue(res, i, i_fnum);
        nptr = PQgetvalue(res, i, n_fnum);
        pptr = PQgetvalue(res, i, p_fnum); /*THIS IS A VALUE I AM TRYING TO GET*/

        /*
        * The binary representation of INT4 is in network byte order, which
        * we'd better coerce to the local byte order.
        */
        ival = ntohl(*((uint32_t *) iptr));

        /*
        * The binary representation of TEXT is, well, text, and since libpq
        * was nice enough to append a zero byte to it, it'll work just fine
        * as a C string.
        *
        * The binary representation of BYTEA is a bunch of bytes, which could
        * include embedded nulls so we have to pay attention to field length.
        */
        //blen = PQgetlength(res, i, b_fnum);

        printf("tuple %d: got\n", i);
        printf(" i = (%d bytes) %d\n",
            PQgetlength(res, i, i_fnum), ival);
        printf(" t = (%d bytes) '%s'\n",
            PQgetlength(res, i, n_fnum), nptr);
        printf(" p = (%d bytes) %f\n",
            PQgetlength(res, i, p_fnum), *reinterpret_cast<double*>(pptr));

        printf("\n\n");
    }
}


int main(int argc, char* argv [])
{
    auto conn_string = "postgresql://postgres:pwd@localhost/db";
    auto conn_deleter = [](PGconn* c) { PQfinish(c); };
    auto res_deleter = [](PGresult* r) { PQclear(r); std::cout << "deleted" << std::endl; };
    std::unique_ptr<PGconn, decltype(conn_deleter)> conn(PQconnectdb(conn_string), conn_deleter);

    if (PQstatus(conn.get()) != ConnStatusType::CONNECTION_OK)
        std::cerr << "Problem" << std::endl;

    std::vector<const char *> params{ "1" };
    std::unique_ptr < PGresult, decltype(res_deleter)> res(PQexecParams(conn.get(),
        "SELECT * FROM table_with_double WHERE id = $1",
        params.size(),       /* one param */
        NULL,    /* let the backend deduce param type */
        (const char * const *)&params.front(),
        NULL,    /* don't need param lengths since text */
        NULL,    /* default to all text params */
        1), /* ask for binary results */
        res_deleter);      

    if (PQresultStatus(res.get()) != ExecStatusType::PGRES_TUPLES_OK)
        std::cout << "SELECT failed: " << PQerrorMessage(conn.get()) << std::endl;
    show_binary_results(res.get());
}
1

There are 1 answers

3
Kimi On BEST ANSWER

Apparently data for a double column comes in as big endian and has to be converted to a little endian. Same as ints. Based on this excellent article from this answer I have used a simple function to swap the double value.

double double_swap(double d)
{
    union
    {
        double d;
        unsigned char bytes[8];
    } src, dest;

    src.d = d;
    dest.bytes[0] = src.bytes[7];
    dest.bytes[1] = src.bytes[6];
    dest.bytes[2] = src.bytes[5];
    dest.bytes[3] = src.bytes[4];
    dest.bytes[4] = src.bytes[3];
    dest.bytes[5] = src.bytes[2];
    dest.bytes[6] = src.bytes[1];
    dest.bytes[7] = src.bytes[0];
    return dest.d;
}

Applying this function a correct value is retrieved from the DB.

printf(" p = (%d bytes) %lf\n",
            PQgetlength(res, i, p_fnum), double_swap(*((double*) pptr)));