QSqlQuery вставляет QByteArray как строку в PostgreSQL

У меня есть таблица PostgreSQL 9.4.5 со столбцом с базовым символьным типом данных, т.е. созданная так:

CREATE TABLE films (
    code        char(5) CONSTRAINT firstkey PRIMARY KEY,
    title       varchar(40) NOT NULL);

Затем я вставляю данные с помощью QSqlQuery, используя связанный QByteArray:

QSqlQuery query;
query.prepare("INSERT INTO films VALUES (1, ?)");
const QByteArray film("Avatar");
query.addBindValue(film);
query.exec();

В Ubuntu 15.10 название фильма заносится в таблицу в виде байтов:

\x417661746172

В Windows это идет как символы.

Можно ли без явного приведения QByteArray к QString указать QSqlQuery или PostgreSQL обрабатывать данные как строку, чтобы они работали в Ubuntu так же, как и в Windows?


person Lozzer    schedule 03.02.2016    source источник
comment
Основной вопрос - есть ли смысл использовать QByteArray вместо QString? В приведенном коде я не вижу ни одного.   -  person klin    schedule 03.02.2016


Ответы (3)


QByteArray не имеет информации о кодировке содержащейся в нем строки (или даже о том, что содержащаяся в ней последовательность байтов может быть интерпретирована как закодированная строка).

Если он содержит строку в кодировке UTF-8, вы можете либо

  • Свяжите его как строку с QString::fromUtf8(film.constData()) вместо просто film

  • пусть драйвер Qt передает его как bytea, но пусть PostgreSQL преобразует его в текст с помощью запроса INSERT:

    query.prepare("INSERT INTO films VALUES (1, convert_from(?,'UTF-8')))");
    

Это также должно работать для других кодировок, например UTF-8 выше.


О разнице между Windows/Ubuntu: непонятно, почему QtSql будет вести себя по-другому, но, возможно, это разница в конфигурации postgres.

'\x417661746172' — это текстовое представление Avatar в UTF-8 в виде двоичной строки, но только когда bytea_output установлено в hex. Если bytea_output установить на escape, это будет точно Avatar и неотличимо от самого текста.

Пример в клиенте командной строки psql:

test=> set bytea_output=hex;
SET
test=> select 'Avatar'::bytea;
     bytea      
----------------
 \x417661746172
(1 row)

test=> set bytea_output=escape;
SET
test=> select 'Avatar'::bytea;
 bytea  
--------
 Avatar
(1 row)

Это экранирование bytea также происходит на стороне клиента в таких драйверах, как QPSQL, а разновидность hex доступна только начиная с PostgreSQL 9.0. До этого единственным методом был escape, а параметра bytea_output не существовало. Я считаю, что простое соединение QtSql с libpq старше 9.0 на компьютере с Windows может объяснить, почему вы получаете «текстовый» внешний вид по сравнению с «шестнадцатеричным» внешним видом в недавней Ubuntu.

person Daniel Vérité    schedule 03.02.2016
comment
convert_from полезно знать, спасибо. Интересно, что в Windows слой QtSql автоматически выполняет преобразование байтов (предположительно в UTF-8), тогда как в Ubuntu это нужно делать явно. - person Lozzer; 03.02.2016
comment
@Lozzer: я добавил возможное объяснение этого в ответ. Я считаю, что на самом деле это не окна против Ubuntu, а скорее разница в версии/конфигурации. - person Daniel Vérité; 03.02.2016

Попробуйте использовать bindValue вместо addBindValue. Перейдите по этой ссылке.

person Nithish    schedule 03.02.2016
comment
Боюсь, тот же результат. - person Lozzer; 03.02.2016

У меня такая же проблема с Qt 5.6 в Windows и PostgresSQL 9.6 в debian. При компиляции с 64-битным bytea читается как двоичный. При компиляции с 32-битным bytea читается как шестнадцатеричный.

CREATE TABLE image (
   id serial,
   name text,
   picture bytea
 );

sql_query.prepare("SELECT id, name, picture FROM image");
...
QByteArray name = sql_query.value("name").toByteArray();
QByteArray Picture = SQL_query.value("Picture").toByteArray();

varchar read with both (32-bit and 64-bit) into QByteArray as string.

//Output 64-Bit application:
name = "Test", Picture = "‰PNG\r\n\x1a\n\0\0..."
//Output 32-Bit application:
Name = "Test", Picture = "x89504e470d0a1a0a0000..."

Я добавил после открытия следующего запроса, и теперь он работает

  QSqlQuery sql_query;
  sql_query.exec("SET bytea_output = 'escape'");

Оба драйвера postgres взяты из стр. 9.5, 64-разрядный — из установленного Postgres, 32-разрядный — загружен в виде zip-файла. Оба имеют одинаковое имя "libpq.dll"

person Gottfried    schedule 09.06.2019