2017-02-20 52 views
2

我使用libpq在表中插入浮點數。我得到這個錯誤INSERT failed: ERROR: insufficient data left in message使用libpq在表中插入浮點數

這裏是我的代碼庫對應的摘錄:

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

回答

1

有兩個錯誤在你的代碼:

  • 您嘗試發送二進制數據,但你不知道PQexecParams它是哪種類型。

    這是行不通的。缺少類型信息,PostgreSQL將使用類型unknown並將其視爲字符串。這意味着您的二進制表示將被饋送到float8in函數,該函數將字符串轉換爲雙精度值,這會導致可怕的失敗。這可能是你正在觀察的。

    你將不得不使用第四個參數與包含701 Oid[](或FLOAT8OID如果你寧願使用PostgreSQL的#define,但你必須#include <postgres.h><catalog/pg_type.h>爲)。

  • 您錯誤地認爲PostgreSQL的double precision類型的二進制表示形式是您客戶機器上使用的double的二進制格式。

    如果您的程序在big-endian機器上運行,這可能會意外地起作用,因爲幾乎每個架構現在都使用IEEE floating point numbers

    如果你讀了源代碼,你會發現,在pq_sendfloat8定義的 PostgreSQL的過度的電線二進制格式IST,這就要求pq_sendint64,其中8個字節的值轉換爲網絡字節順序(這是與大端表示法相同)。

所以你必須定義一個類似的轉換功能:

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

那麼你的代碼看起來是這樣的:

Oid types[1]; 
double converted; 

... 

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

但坦率地說,這將是更更容易使用文本表示。這將使你的代碼獨立於PostgreSQL內部,並且可能不會太慢​​。

它看起來並不像它,但如果double precision值從一個PostgreSQL表別的地方拉,你可以設置extra_float_digits= 3,讓你保證不會丟失任何精度當值轉換爲他們的字符串表示..

+0

第一點:testlibpq2.c說ergarding OID「讓後端推斷參數類型」爲二進制和文本的例子。這就是我省略它。你的第二點是有效的。 – Jam

+0

你認爲後端應該推斷出你傳遞的4個字節是雙精度? [文檔](https://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQEXECPARAMS)說:*如果paramTypes爲NULL,或者數組中的任何特定元素爲零,則服務器推斷參數符號的數據類型的方式與它對**無類型文字字符串**。(強調我的) –