PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Einfügen einer Fließkommazahl in eine Tabelle mit libpq

Ihr Code enthält zwei Fehler:

  • Sie versuchen, binäre Daten zu senden, aber Sie teilen PQexecParams nichts mit um welchen Typ es sich handelt.

    Das kann nicht funktionieren. Ohne Typinformationen verwendet PostgreSQL den Typ unknown und behandeln Sie es als Zeichenfolge. Das bedeutet, dass Ihre binäre Darstellung dem float8in zugeführt wird Funktion, die Zeichenfolgen in Werte mit doppelter Genauigkeit umwandelt, was schrecklich fehlschlagen wird. Dies ist wahrscheinlich das, was Sie beobachten.

    Sie müssen einen vierten Parameter mit einem Oid[] verwenden die 701 (oder FLOAT8OID) enthält wenn Sie lieber #define von PostgreSQL verwenden möchten , aber Sie müssten #include <postgres.h> und <catalog/pg_type.h> dafür).

  • Sie nehmen fälschlicherweise an, dass die binäre Darstellung von PostgreSQL die double precision type ist das Binärformat für double auf Ihrem Client-Rechner verwendet wird.

    Dies könnte versehentlich funktionieren, wenn Ihr Programm auf einem Big-Endian läuft Maschine, da praktisch jede Architektur heutzutage IEEE-Gleitkommazahlen verwendet .

    Wenn Sie den Quellcode lesen, werden Sie feststellen, dass das Over-the-Wire-Binärformat von PostgreSQL in pq_sendfloat8 definiert ist in src/backend/libpq/pqformat.c , die pq_sendint64 aufruft , der den 8-Byte-Wert in die Netzwerk-Byte-Reihenfolge konvertiert (was der Big-Endian-Darstellung entspricht).

Sie müssten also eine Konvertierungsfunktion ähnlich der folgenden definieren:

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

Dann könnte Ihr Code so aussehen:

Oid types[1];
double converted;

...

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

Aber ehrlich gesagt wäre es viel einfacher, die Textdarstellung zu verwenden. Das macht Ihren Code unabhängig von PostgreSQL-Interna und ist wahrscheinlich nicht so viel langsamer.

Es sieht nicht so aus, aber wenn die double precision Werte aus einer PostgreSQL-Tabelle an anderer Stelle gezogen werden, könnten Sie extra_float_digits = 3 damit Sie garantiert keine Genauigkeit verlieren, wenn die Werte in ihre String-Darstellung konvertiert werden..