PostgreSQL libpq Ошибка целого числа вне диапазона при отправке целого числа как двоичного

Я пытаюсь вставить некоторые целые числа в таблицу Postgres с помощью следующего несколько простого кода.

#include <libpq-fe.h>
#include <stdio.h>
#include <stdint.h>

int main() {
  int64_t i = 0;
  PGconn * connection = PQconnectdb( "dbname='babyfood'" );
  if( !connection || PQstatus( connection ) != CONNECTION_OK )
    return 1;
  printf( "Number: " );
  scanf( "%d", &i );

  char * params[1];
  int param_lengths[1];
  int param_formats[1];
  param_lengths[0] = sizeof( i );
  param_formats[0] = 1;
  params[0] = (char*)&i;
  PGresult * res =  PQexecParams( connection, 
                                  "INSERT INTO intlist VALUES ( $1::int8 )",
                                  1,
                                  NULL,
                                  params,
                                  param_lengths,
                                  param_formats,
                                  0 );
  printf( "%s\n", PQresultErrorMessage( res ) );
  PQclear( res );
  PQfinish( connection );
  return 0;
}

Я получаю следующие результаты:

Number: 55
ERROR:  integer out of range
Number: 1
ERROR:  integer out of range

Я почти уверен, что int64_t всегда будет соответствовать 8-байтовому целому числу на любой разумной платформе. Что я делаю неправильно?


person Edward    schedule 19.08.2009    source источник


Ответы (3)


Вместо:

params[0] = (char*)&i;

вы должны использовать:

#include <endian.h>
/* ... */
int64_t const i_big_endian = htobe64(i);
params[0] = (char*)&i_big_endian;

Функция htobe64 переключит порядок следования байтов на обратный порядок байтов и ничего не сделает на обратный порядок байтов.

Откажитесь от функции flip_endian, так как это сделает вашу программу несовместимой с компьютерами с прямым порядком байтов/двумя порядковыми байтами, такими как PowerPC, Alpha, Motorola, SPARC, IA64 и т. д. Даже если ваша программа не предполагает запуска на них, это плохо. стиль, медленный и подверженный ошибкам.

person Tometzky    schedule 20.08.2009

Хорошо, кажется, что это проблема с порядком байтов, которая все еще не совсем объясняет ее, поскольку 64-битное целое число со знаком с прямым порядком байтов (т. е. x86) должно соответствовать 64-битному целому с обратным порядком байтов, и наоборот, они просто быть испорченным. Однако замена порядка байтов на целое число дает правильное значение. Обмен осуществляется с помощью следующей функции:

int64_t flip_endian( int64_t endi ) {
  char* bytearray;
  char swap;
  int64_t orig = endi;
  int i;

  bytearray = (char*)&orig;

  for( i = 0; i < sizeof( orig )/2; ++i ) {
    swap = bytearray[i];
    bytearray[i] = bytearray[ sizeof( orig ) - i - 1 ];
    bytearray[ sizeof( orig ) - i - 1 ] = swap;
  }

  return orig;

}
person Edward    schedule 19.08.2009

Я думаю, что он передается как 32-битный int, а затем преобразуется в 64-битный, потому что вы не сообщаете libpq, что такое формат.

Попробуйте указать массив для paramTypes с oid для int8 (который равен 20) для параметра.

person Magnus Hagander    schedule 19.08.2009
comment
Предполагается, что это можно определить по приведению ::int8. Кроме того, это определенно проблема с порядком байтов, поскольку изменение порядка байтов не только избавляет от ошибки, но и приводит к правильным вставкам. - person Edward; 20.08.2009
comment
Ах я вижу. Я должен был подумать об этом :) - person Magnus Hagander; 20.08.2009