使用 libpq 在表中插入浮点数

Jam*_*Jam 3 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);
Run Code Online (Sandbox Code Playgroud)

Lau*_*lbe 5

您的代码中有两个错误:

  • 您正在尝试发送二进制数据,但您不知道PQexecParams它是哪种类型。

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

    你将不得不使用与第四个参数Oid[]包含701(或者FLOAT8OID,如果你宁愿使用PostgreSQL的#define,但你必须#include <postgres.h><catalog/pg_type.h>该)。

  • 您错误地认为 PostgreSQL 的二进制表示double precision类型是double在您的客户端机器上使用的二进制格式。

    如果您的程序在大端机器上运行,这可能会意外地工作,因为如今几乎所有架构都使用IEEE 浮点数

    如果您阅读源代码,您会发现 PostgreSQL 的在线二进制格式定义在pq_sendfloat8src/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);
}
Run Code Online (Sandbox Code Playgroud)

那么您的代码可能如下所示:

Oid types[1];
double converted;

...

types[0] = FLOAT8OID;
to_nbo(value, &converted);
values[0] = (char *)&converted;
Run Code Online (Sandbox Code Playgroud)

但坦率地说,使用文本表示会容易得多。这将使您的代码独立于 PostgreSQL 内部结构,并且可能不会那么慢。

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