2017-02-20 2 views
2

Ich füge eine Gleitkommazahl in eine Tabelle mit libpq ein. Ich erhalte diesen Fehler INSERT failed: ERROR: insufficient data left in message.Einfügen einer Fließkommazahl in eine Tabelle mit Hilfe von libpq

Hier ist der entsprechende Ausschnitt aus meiner Code-Basis:

printf ("Enter write parameter_value"); 
scanf("%f", &parameter_value); 

char *stm = "INSERT INTO write_reg_set (parameter_value) VALUES ($1::double precision)"; 

int nparam = 1; 

//set the values to use 
const char *values[1] = {(char *)&parameter_value}; 

//calculate the lengths of each of the values 
int lengths[1] = {sizeof(parameter_value)}; 

//state which parameters are binary 
int binary[1] = {1}; 

PGresult *res = PQexecParams(conn, 
        stm, 
        nparam, //number of parameters 
        NULL, //ignore the Oid field 
        values, //values to substitute $1 and $2 and so on 
        lengths, //the lengths, in bytes, of each of the parameter values 
        binary, //whether the values are binary or not 
        0);  //we want the result in text format 

if (PQresultStatus(res) != PGRES_COMMAND_OK) { 
    fprintf(stderr, "INSERT failed: %s", PQerrorMessage(conn)); 
    exit_nicely(conn,res); 
} 
PQclear(res); 

Antwort

1

Es gibt zwei Fehler im Code:

  • Sie versuchen, binäre Daten zu senden, aber nicht sagen, PQexecParams welcher Typ ist es.

    Das kann nicht funktionieren. Aufgrund fehlender Typinformationen verwendet PostgreSQL den Typ unknown und behandelt ihn als String. Das bedeutet, dass Ihre Binärdarstellung an die float8in-Funktion übergeben wird, die Strings in Werte mit doppelter Genauigkeit konvertiert, was schief läuft. Dies ist wahrscheinlich, was Sie beobachten.

    Sie haben einen vierten Parameter mit einem Oid[] verwenden, die 701 enthält (oder FLOAT8OID, wenn Sie lieber #define PostgreSQLs verwenden würde, aber man müsste #include <postgres.h> und <catalog/pg_type.h> dafür).

  • Sie irren irrtümlich davon aus, dass PostgreSQLs binäre Darstellung des Typs double precision das Binärformat für double ist, das auf Ihrem Clientcomputer verwendet wird.

    Dies könnte versehentlich funktionieren, wenn Ihr Programm auf einem big-endian-Rechner ausgeführt wird, da praktisch jede Architektur heutzutage IEEE floating point numbers verwendet.

    Wenn Sie den Quellcode lesen, werden Sie PostgreSQLs Over-the-Wire-Binärformat ist definiert in pq_sendfloat8 in feststellen, dass, was pq_sendint64 aufruft, die den 8-Byte-Wert konvertiert Byte-Reihenfolge zu vernetzen (die ist dasselbe wie Big-Endian-Darstellung).

So würden Sie eine Konvertierungsfunktion ähnlich wie diese definieren müssen:

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 wird Ihr Code wie folgt aussehen könnte:

Oid types[1]; 
double converted; 

... 

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

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

Es ist nicht, wie es aussieht, aber wenn die double precision Werte aus einer PostgreSQL-Tabelle sonst irgendwo gezogen werden, können Sie extra_float_digits= 3 so eingestellt, dass Sie garantiert keine Präzision zu verlieren, wenn die Werte in ihre String-Darstellung umgewandelt werden ..

+0

1. Punkt: testlibpq2.c sagt ergarding OID "Lassen Sie das Back-End Param-Typ abzuleiten" für beide Binär-und Text-Beispiele. Deshalb habe ich es weggelassen. Ihr 2. Punkt ist gültig. – Jam

+0

Wie denkst du, sollte das Backend folgern, dass 4 Bytes, die du passierst, ein Double sind? [Die Dokumentation] (https://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQEXECPARAMS) sagt: * Wenn paramTypes NULL ist, oder ein bestimmtes Element im Array ist Null, die Server leitet einen Datentyp für das Parametersymbol auf die gleiche Weise wie für eine ** untypisierte Literalfolge **. * (Hervorhebung meins) –

Verwandte Themen