3

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());
}
Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
Kimi
  • 13,621
  • 9
  • 55
  • 84
  • How do you call PQgetvalue ? – Stephane Rolland Dec 10 '13 at 20:06
  • AFAIK, `text` and `bytea` are basically glorified `char*` at this level so there is no endianness to worry about, similarly the bit layout of an IEEE double is machine-independent so there's no endianness there either. – mu is too short Dec 11 '13 at 00:33
  • @muistooshort, that is what I would expect, but the double value I'm getting is 0. – Kimi Dec 11 '13 at 09:36
  • @StephaneRolland, I have posted the code. – Kimi Dec 11 '13 at 09:36
  • here you do an `atof` to try converting the result value , it is not a cast ! could you post the code related to your question, i.e. with the **reinterpret_cast to float** that you tried ? – Stephane Rolland Dec 11 '13 at 12:11
  • @StephaneRolland, edited. I have tried many silly things with no luck. Clearly I don't know what to do. – Kimi Dec 11 '13 at 12:34
  • could you check that `PQgetlength(res, i, p_fnum);` is 4 just in case there are trouble with the definition of your column ? Otherwise, have you checked in your database that your record has really a field different from 0.0 for this record ( print the id to be sure to point to the good result) ? – Stephane Rolland Dec 11 '13 at 13:02
  • here is a question where I asked about the endianness troubles with doubles in libpq. The answers may help you. http://stackoverflow.com/q/15079463/356440 . – Stephane Rolland Dec 11 '13 at 13:06
  • sorry I made a mistake, you should check PQgetlength(res, i, p_fnum); is 8 and not 4, since your asking for a double value, and not float. – Stephane Rolland Dec 11 '13 at 13:37
  • @StephaneRolland, the length is 8 bytes, it is a double float 64 bits. The int and string fields are retrieved correctly, so the record is correct. – Kimi Dec 11 '13 at 13:37

1 Answers1

4

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)));
Community
  • 1
  • 1
Kimi
  • 13,621
  • 9
  • 55
  • 84
  • please also consider using `std::cout <<` rather than `printf`. It is considered C++ instead of C. It also does all commonplace conversions on the fly. – Stephane Rolland Dec 11 '13 at 20:56
  • @StephaneRolland, this is a code from the libpq examples, this way I'm aiming to show that I'm not doing anything unusual. I agree that this code should not pass any code reviews. – Kimi Dec 12 '13 at 09:43
  • Since OP explicitly mentioned MS Windows (though not 8), there is [htonll](https://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx) for swapping byte order on 64 bit long integers / blindly casted doubles. – mlt May 19 '15 at 04:40