PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Inserimento di un numero in virgola mobile in una tabella usando libpq

Ci sono due errori nel tuo codice:

  • Stai tentando di inviare dati binari, ma non dici a PQexecParams che tipo è.

    Non può funzionare. In mancanza di informazioni sul tipo, PostgreSQL utilizzerà il tipo unknown e trattalo come una stringa. Ciò significa che la tua rappresentazione binaria verrà inviata a float8in funzione che converte le stringhe in valori a doppia precisione, che falliranno in modo orribile. Questo è probabilmente ciò che stai osservando.

    Dovrai usare un quarto parametro con un Oid[] che contiene 701 (o FLOAT8OID se preferisci usare #define di PostgreSQL , ma dovresti #include <postgres.h> e <catalog/pg_type.h> per quello).

  • Si presume erroneamente che la rappresentazione binaria di PostgreSQL della double precision type è il formato binario per double in uso sul tuo computer client.

    Questo potrebbe funzionare accidentalmente se il tuo programma è in esecuzione su un big-endian macchina, dal momento che praticamente ogni architettura oggigiorno utilizza numeri in virgola mobile IEEE .

    Se leggi il codice sorgente, scoprirai che il formato binario over-the-wire di PostgreSQL è definito in pq_sendfloat8 in src/backend/libpq/pqformat.c , che chiama pq_sendint64 , che converte il valore a 8 byte nell'ordine dei byte di rete (che è lo stesso della rappresentazione big-endian).

Quindi dovresti definire una funzione di conversione simile a questa:

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

Quindi il tuo codice potrebbe assomigliare a questo:

Oid types[1];
double converted;

...

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

Ma francamente, sarebbe molto più semplice utilizzare la rappresentazione testuale. Ciò renderà il tuo codice indipendente dagli interni di PostgreSQL e probabilmente non sarà molto più lento.

Non sembra, ma se la double precision i valori vengono estratti da una tabella PostgreSQL da qualche altra parte, è possibile impostare extra_float_digits = 3 in modo che tu abbia la garanzia di non perdere alcuna precisione quando i valori vengono convertiti nella loro rappresentazione di stringa..