Вставка числа с плавающей запятой в таблицу с помощью 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);

person Jumshed Akhtar    schedule 20.02.2017    source источник


Ответы (1)


В вашем коде есть две ошибки:

  • Вы пытаетесь отправить двоичные данные, но не указываете PQexecParams, какой это тип.

    Это не может сработать. Не имея информации о типе, PostgreSQL будет использовать тип unknown и рассматривать его как строку. Это означает, что ваше двоичное представление будет передано в функцию float8in, которая преобразует строки в значения с двойной точностью, что приведет к ужасному сбою. Вероятно, это то, что вы наблюдаете.

    Вам нужно будет использовать четвертый параметр с Oid[], который содержит 701 (или FLOAT8OID, если вы предпочитаете использовать #define PostgreSQL, но для этого вам нужно будет #include <postgres.h> и <catalog/pg_type.h>).

  • Вы ошибочно предполагаете, что двоичное представление PostgreSQL типа double precision - это двоичный формат для double, используемый на вашем клиентском компьютере.

    Это может случайно сработать, если ваша программа запущена на машине big-endian, поскольку практически каждая архитектура days использует числа с плавающей запятой IEEE.

    Если вы прочитаете исходный код, вы обнаружите, что двоичный формат PostgreSQL по беспроводной сети определен в pq_sendfloat8 в src/backend/libpq/pqformat.c, который вызывает pq_sendint64, который преобразует 8-байтовое значение в сетевой порядок байтов (который совпадает с большим- 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 и, вероятно, будет не намного медленнее.

Это не похоже, но если значения double precision взяты из таблицы PostgreSQL где-то еще, вы можете установить _ 17_ = 3, чтобы вы гарантированно не потеряли точность при преобразовании значений в их строковое представление.

person Laurenz Albe    schedule 20.02.2017
comment
1-й пункт: testlibpq2.c говорит, что ergarding OID позволяет бэкэнду определять тип параметра как для двоичных, так и для текстовых примеров. Вот почему я его пропустил. ваш 2-й балл действителен. - person Jumshed Akhtar; 21.02.2017
comment
Как вы думаете, как бэкэнд должен сделать вывод, что 4 байта, которые вы ему передали, являются двойными? В документации говорится: Если paramTypes имеет значение NULL или любой конкретный элемент в массиве равен нулю, сервер определяет тип данных для символа параметра точно так же, как это было бы для нетипизированной литеральной строки. (курсив моя) - person Laurenz Albe; 21.02.2017