2017-02-20 4 views
2

J'insère un nombre à virgule flottante dans une table à l'aide de libpq. Je reçois cette erreur INSERT failed: ERROR: insufficient data left in message.Insertion d'un nombre à virgule flottante dans une table à l'aide de libpq

Voici l'extrait correspondant de ma base de code:

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

Répondre

1

Il y a deux erreurs dans votre code:

  • Vous essayez d'envoyer des données binaires, mais vous ne dites pas PQexecParams quel type il est.

    Cela ne peut pas fonctionner. En l'absence d'informations de type, PostgreSQL utilisera le type unknown et le traitera comme une chaîne. Cela signifie que votre représentation binaire sera transmise à la fonction float8in qui convertit les chaînes en valeurs de double précision, ce qui échouera horriblement. C'est probablement ce que vous observez.

    Vous devrez utiliser un quatrième paramètre avec un Oid[] qui contient 701 (ou FLOAT8OID si vous préférez utiliser PostgreSQL #define de, mais vous auriez à #include <postgres.h> et <catalog/pg_type.h> pour cela). Vous supposez à tort que la représentation binaire de PostgreSQL du type double precision est le format binaire pour double utilisé sur votre machine client.

    Ceci pourrait accidentellement fonctionner si votre programme fonctionne sur une machine big-endian, puisque pratiquement toutes les architectures actuelles utilisent IEEE floating point numbers.

    Si vous lisez le code source, vous constaterez que le format binaire sur le fil ist PostgreSQL défini dans pq_sendfloat8 dans , qui appelle pq_sendint64, qui convertit la valeur de 8 octets au réseau ordre d'octet (ce qui est le même que la représentation big-endian).

Il faudrait donc définir une fonction de conversion similaire à ceci:

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

Ensuite, votre code pourrait ressembler à ceci:

Oid types[1]; 
double converted; 

... 

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

Mais franchement, il serait beaucoup plus facile d'utiliser la représentation du texte. Cela rendra votre code indépendant des internes de PostgreSQL et n'est probablement pas tellement plus lent.

Il ne ressemble pas, mais si les double precision valeurs sont tirées d'une table PostgreSQL ailleurs, vous pouvez définir extra_float_digits= 3 de sorte que vous êtes assuré de ne pas perdre toute précision lorsque les valeurs sont converties en leur représentation de chaîne

+0

1er point: testlibpq2.c dit ergarding OID "laisser le backend en déduire le type param" pour les deux binaires et des exemples de texte. Thatswhy je l'ai omis. votre 2ème point est valide. – Jam

+0

Comment pensez-vous que le backend supposé déduire que 4 octets vous le passez sont un double? [La documentation] (https://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQEXECPARAMS) dit: * Si paramTypes est NULL, ou si un élément particulier du tableau est zéro, le le serveur déduit un type de données pour le symbole de paramètre de la même manière que pour une chaîne littérale ** non typée **. –