使用libpq在表中插入浮点数

时间:2017-02-20 08:34:25

标签: c postgresql libpq

我使用libpq在表格中插入浮点数。我收到此错误INSERT failed: ERROR: insufficient data left in message

以下是我的代码库中的相应代码段:

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 个答案:

答案 0 :(得分:1)

您的代码中有两个错误:

  • 您正在尝试发送二进制数据,但您没有告诉PQexecParams它是哪种类型。

    那不行。缺少类型信息,PostgreSQL将使用类型unknown并将其视为字符串。这意味着您的二进制表示将被馈送到float8in函数,该函数将字符串转换为双精度值,这将失败。这可能就是你所观察到的。

    你必须使用包含701的Oid[]的第四个参数(或者如果您更喜欢使用PostgreSQL的FLOAT8OID,则使用#define,但是您必须{{1} }}和#include <postgres.h>。)

  • 您错误地认为PostgreSQL的<catalog/pg_type.h>类型的二进制表示形式是客户端计算机上使用的double precision的二进制格式。

    如果您的程序在big-endian计算机上运行,​​这可能会无意中发生,因为现在几乎每个架构都使用IEEE floating point numbers

    如果您阅读了源代码,您会发现PostgreSQL的线上二进制格式是在double pq_sendfloat8中定义的,src/backend/libpq/pqformat.c调用pq_sendint64,后者转换为8 - 网络字节顺序的字节值(与big-endian表示相同)。

所以你必须定义一个类似于此的转换函数:

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内部,并且可能不会那么慢。

它看起来不像,但如果从其他地方的PostgreSQL表中提取double precision值,您可以设置extra_float_digits = 3,以确保不会丢失将值转换为字符串表示形式时的任何精度。