How to get the double value using libpq?

c++, double, endianness, libpq, postgresql

Solution

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)));

Problem

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()); } ```

Original source

Related problems