Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt Разработка баз данных _ QSqlQuery не позволяет загружать в БД большие бинарники

Автор: Stanislaus 3.9.2008, 15:54

Маленькие файлики записываются без проблем, хотя и подвисает операция вставки секунд на 5...

При попытке записать в БД бинарник большого размера (напр. около 30 мегабайт)...

    q.prepare("select file_share.add_single_file(?, ?, ?, ?, ?, ?, null,?,?);");
    QByteArray ba;
    QFile file(getFilePath());
    if (file.open(QIODevice::ReadOnly)){
        ba = file.readAll();
        file.close();
    }
    q.bindValue(0,ba,QSql::In|QSql::Binary); // Здесь все пучком
    q.bindValue(1,getGroupID());
    if (getInsurer() != 0) q.bindValue(2,getInsurer());
    else q.bindValue(2,QVariant(QVariant::Int));
    q.bindValue(3,getDescription());
    q.bindValue(4,getFileName());
    q.bindValue(5,getWWW());
    q.bindValue(6,getCity());
    q.bindValue(7,mask);
    if (!q.exec()) { // тут виснет секунд на 30 и Unhandled exception at 0x1026f3e0 in project.exe: 0xC0000005: Access violation reading location 0xf272f6c4.
        qDebug() << q.lastError() << q.executedQuery();
        return false;
    }


Далее следует виновник эксепшена...

QString& QString::insert(int i, const QChar *unicode, int size)
{
    if (i < 0 || size <= 0)
        return *this;

    const ushort *s = (const ushort *)unicode;
    if (s >= d->data && s < d->data + d->alloc) {
        // Part of me - take a copy
        ushort *tmp = static_cast<ushort *>(qMalloc(size * sizeof(QChar)));
        memcpy(tmp, s, size * sizeof(QChar));
        insert(i, reinterpret_cast<const QChar *>(tmp), size);
        qFree(tmp);
        return *this;
    }

    expand(qMax(d->size, i) + size - 1);

    ::memmove(d->data + i + size, d->data + i, (d->size - i - size) * sizeof(QChar)); // <--- Эксепшн вылетает во время этой операции
    memcpy(d->data + i, s, size * sizeof(QChar));
    return *this;
}


Подключение к БД
QSqlDatabase db_b = QSqlDatabase::addDatabase("QPSQL", "b"); 
db_b.setHostName("10.10.1.123");
db_b.setPort(5433);
db_b.setDatabaseName("DBName");
db_b.setUserName("user");
db_b.setPassword("password");


Если кто сталкивался, помогите плиз.

Собираю на Microsoft Visual Studion 2008 Express Edition, Qt 4.4.0 Open Source.

Автор: Litkevich Yuriy 3.9.2008, 16:53

Цитата(Stanislaus @ 3.9.2008, 19:54) *
При попытке записать в БД бинарник большого размера (напр. около 30 мегабайт)...
а БД поддерживает такой размер?

Автор: Stanislaus 3.9.2008, 17:30

Цитата(Litkevich Yuriy @ 3.9.2008, 17:53) *
а БД поддерживает такой размер?


Из того, что мне удалось найти, bytea поддерживает примерно до 1Gb (http://www.postgresql.org/docs/7.4/interactive/datatype-binary.html), поэтому смею предположить, что тут проблема не в ограничениях БД (PostgreSQL 8.2.*), а в ограничениях Qt... Может быть ограничения QString, который используется в QSqlQuery?


В принципе, как обходной вариант, можно раздробить файл на фрагменты, записать фрагментами в БД и там уже сконкантенировать в единое целое, но уж очень удобно было бы, если бы он единым целым сразу записывался.

Автор: Litkevich Yuriy 3.9.2008, 17:37

вобще непонятно причем здесь QString. Вдь ты использушь двоичный масив и поидее должна вызыватся функция:
http://crossplatform.ru/documentation/qtdoc4.3/qsqlquery.php#bindValue-2в ней нет QString

Я незнаком с Постгре, но есть такая заметка:

Binary Large Objects (BLOB) are supported through the BYTEA field type in PostgreSQL server versions >= 7.1.

Может тебе понятно о чем речь?

Автор: Stanislaus 3.9.2008, 17:43

Цитата(Litkevich Yuriy @ 3.9.2008, 18:32) *
вобще непонятно причем здесь QString. Вдь ты использушь двоичный масив и поидее должна вызыватся функция:
http://crossplatform.ru/documentation/qtdoc4.3/qsqlquery.php#bindValue-2в ней нет QString

Именно она и вызывается. Но, насколько я понимаю, QSqlQuery пытается скомпоновать запрос из

select file_share.add_single_file(?, ?, ?, ?, ?, ?, null,?,?);


в

select file_share.add_single_file('\\134\\104\\134.........', 2, 3, 'Commentary is here...', 'file.bmp', true, null,1,0);


что в итоге окажется QString, который отправляется на сервер, но QString в случае большого файла вылезает за какие-то нормы или открывает некий баг... или все по другому происходит?

Автор: Litkevich Yuriy 3.9.2008, 17:45

И к стати, ты не пробывал вызвать такую функцию:
db_b.driver().hasFeature (QSqlDriver::BLOB)
что он скажет, имеет драйвер такую возможность или нет?
Может она толком не реализована.

Автор: Stanislaus 3.9.2008, 18:10

Цитата(Litkevich Yuriy @ 3.9.2008, 18:45) *
И к стати, ты не пробывал вызвать такую функцию:
db_b.driver().hasFeature (QSqlDriver::BLOB)
что он скажет, имеет драйвер такую возможность или нет?
Может она толком не реализована.


db_b.driver()->hasFeature(QSqlDriver::BLOB) возвращает true


Вот к чему привел дебаггинг... Во время обработки первого параметра, которым является бинарник....
bool QSqlResult::exec()
{
    bool ret;
    // fake preparation - just replace the placeholders..
    QString query = lastQuery();
    if (d->binds == NamedBinding) {
        int i;
        QVariant val;
        QString holder;
        for (i = d->holders.count() - 1; i >= 0; --i) {
            holder = d->holders.at(i).holderName;
            val = d->values.value(d->indexes.value(holder));
            QSqlField f(QLatin1String(""), val.type());
            f.setValue(val);
            query = query.replace(d->holders.at(i).holderPos,
                                   holder.length(), driver()->formatValue(f));
        }
    } else {
        QString val;
        int i = 0;
        int idx = 0;
        for (idx = 0; idx < d->values.count(); ++idx) {
            i = query.indexOf(QLatin1Char('?'), i);
            if (i == -1)
                continue;
            QVariant var = d->values.value(idx);
            QSqlField f(QLatin1String(""), var.type());
            if (var.isNull())
                f.clear();
            else
                f.setValue(var);
            val = driver()->formatValue(f); // тут ненадолго задумывается....
            query = query.replace(i, 1, driver()->formatValue(f)); // и тут вылетает ексепшн...
            i += val.length();
        }
    }

    // have to retain the original query with placeholders
    QString orig = lastQuery();
    ret = reset(query);
    d->executedQuery = query;
    setQuery(orig);
    d->resetBindCount();
    return ret;
}

Автор: ViGOur 3.9.2008, 18:20

Цитата(Stanislaus @ 3.9.2008, 16:54) *
::memmove(d->data + i + size, d->data + i, (d->size - i - size) * sizeof(QChar)); // <--- Эксепшн вылетает во время этой операции
Что-то мне не нравится этот код, а ты уверен, что не выходишь за область выделенной памяти? :)

Может быть так:
::memmove(d->data + i + size, d->data + i, (d->size - size) * sizeof(QChar));
?
И какого типа у тебя d->data?

Извини, отставить d->size - size, перепутал местами Src и Dest... :)

Проверь выходит ли за область и всегда ли в i или size правильные значения.

Автор: Litkevich Yuriy 3.9.2008, 18:41

Цитата(ViGOur @ 3.9.2008, 22:20) *
Что-то мне не нравится этот код, а ты уверен, что не выходишь за область выделенной памяти?
а это код Тролей :)

Цитата(Stanislaus @ 3.9.2008, 22:10) *
val = driver()->formatValue(f); // тут ненадолго задумывается....
query = query.replace(i, 1, driver()->formatValue(f)); // и тут вылетает ексепшн...
мабуть драйвер таки не доделаный, надо у тролей в трекере поискать

Автор: Tonal 4.9.2008, 11:28

Если верить этому коду, то Qt вместо параметров явным образом подставляет значения перед отправкой SQL-я на сервер.
А на длину SQL-я могут быть довольно сильные ограничения (Firebird/Interbase < 32к).

Если это так, то с лучше с базой работать сторонними либами, которые умеют работать с честными параметрическими запросами сервера... :(

Автор: Litkevich Yuriy 4.9.2008, 12:29

Цитата(Tonal @ 4.9.2008, 15:28) *
Если это так, то с лучше с базой работать сторонними либами, которые умеют работать с честными параметрическими запросами сервера...
А может троли не спроста привели пример использования родного драйвера Постгре, http://crossplatform.ru/documentation/qtdoc4.3/qsqldatabase.php#addDatabase-2

Автор: Digger86 30.11.2012, 15:31

Добрый день! столкнулся с полностью аналогичной проблемой. Есть ли какие-то способы решения?
использую qt 4.6.4, postgresql 8.4, visual studio 2008.

Автор: RazrFalcon 30.11.2012, 23:23

Может стоит обовиться? (и не поднимать темы 4-х летней давности...)

Qt 4.8.3, linux, firebird
100, 200 mb файлы хавает на раз


Автор: Digger86 6.12.2012, 17:36

к сожалению использование Qt 4.6.4 требование заказчика, так что пляшем от чего имеем..
сделал добавление бинарников непосредственно через libpq

Автор: Litkevich Yuriy 7.12.2012, 9:59

Цитата(Digger86 @ 6.12.2012, 19:36) *
сделал добавление бинарников непосредственно через libpq
а код можешь привести? вдруг ещё кому-нибудь пригодится.

Автор: Digger86 14.12.2012, 11:55

по сути сделал как в примере Example 30-3
http://www.postgresql.org/docs/8.4/static/libpq-example.html

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)