PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Inserindo um número de ponto flutuante em uma tabela usando libpq


Há dois erros no seu código:

  • Você está tentando enviar dados binários, mas não informa PQexecParams que tipo é.

    Isso não pode funcionar. Na falta de informações de tipo, o PostgreSQL usará o tipo unknown e tratá-lo como string. Isso significa que sua representação binária será alimentada no float8in função que converte strings em valores de precisão dupla, que falharão horrivelmente. Provavelmente é isso que você está observando.

    Você terá que usar um quarto parâmetro com um Oid[] que contém 701 (ou FLOAT8OID se você preferir usar o #define do PostgreSQL , mas você teria que #include <postgres.h> e <catalog/pg_type.h> por isso).

  • Você erroneamente assume que a representação binária do PostgreSQL da double precision type é o formato binário para double em uso em sua máquina cliente.

    Isso pode funcionar acidentalmente se seu programa estiver sendo executado em um big-endian máquina, já que praticamente todas as arquiteturas atualmente usam números de ponto flutuante IEEE .

    Se você ler o código-fonte, verá que o formato binário over-the-wire do PostgreSQL está definido em pq_sendfloat8 em src/backend/libpq/pqformat.c , que chama pq_sendint64 , que converte o valor de 8 bytes em ordem de bytes de rede (que é o mesmo que representação big-endian).

Então você teria que definir uma função de conversão semelhante a esta:
static void to_nbo(double in, double *out) {
    uint64_t *i = (uint64_t *)&in;
    uint32_t *r = (uint32_t *)out;

    /* convert input to network byte order */
    r[0] = htonl((uint32_t)((*i) >> 32));
    r[1] = htonl((uint32_t)*i);
}

Então seu código pode ficar assim:
Oid types[1];
double converted;

...

types[0] = FLOAT8OID;
to_nbo(value, &converted);
values[0] = (char *)&converted;

Mas, francamente, seria muito mais fácil usar a representação de texto. Isso tornará seu código independente dos componentes internos do PostgreSQL e provavelmente não será muito mais lento.

Não parece, mas se a double precision os valores são extraídos de uma tabela PostgreSQL em outro lugar, você pode definir extra_float_digits = 3 para que você tenha a garantia de não perder nenhuma precisão quando os valores forem convertidos em sua representação de string.