0
votes

I am rather new to Qt, maybe that's why I cant fully understand the child-parent concept. I need to perform some sql query. I set the QSqlQuery, perform the "prepare and bind" operation and exec it. Next I pass it to the model and display the data. The problem occurs when closing the window - I get a memory violation error. The error occurs only, when I create the model with a parent. Here's the code:

QSqlQuery query;
query.prepare(QString("SELECT \
        %1 as nazwa \
        , kontrahentid \
        FROM kontrahent WHERE %2 ilike ?"
    ).arg(showWhat, searchBy) //handled above, no need to escape
);
query.addBindValue(searchString); //user input data - so bind it

if (!query.exec()) {
    qDebug() << query.lastError();
    QApplication::restoreOverrideCursor();
    return;
}

if (model == NULL)
//  model = new QSqlQueryModel; // app closes the window correctly
    model = new QSqlQueryModel(this); // app crashes when closing the window

model->setQuery(query);
if (model->lastError().isValid()) {
    qDebug() << model->lastError();
    QApplication::restoreOverrideCursor();
    return;
}

model->setHeaderData(0, Qt::Horizontal, "ID");
ui.kontrahenciList->setModel(model);
//ui.kontrahenciList->setModelColumn(1);
ui.kontrahenciList->show();

Here's the error I'm getting:

Unhandled exception at 0x0fe29f9a (qsqlpsqld.dll) in HurBudClientGUI.exe: 0xC0000005: Access violation reading location 0x00000004.

and the call stack:

qsqlpsqld.dll!QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> >::data()  Line 143 + 0x3 bytes C++
qsqlpsqld.dll!qGetPtrHelper<QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> > >(const QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> > & p)  Line 919 + 0xb bytes  C++
qsqlpsqld.dll!QPSQLDriver::d_func()  Line 106 + 0x13 bytes  C++
qsqlpsqld.dll!QPSQLResultPrivate::privDriver()  Line 212    C++
qsqlpsqld.dll!QPSQLResultPrivate::deallocatePreparedStmt()  Line 306 + 0xc bytes    C++
qsqlpsqld.dll!QPSQLResult::~QPSQLResult()  Line 328 C++
qsqlpsqld.dll!QPSQLResult::`scalar deleting destructor'()  + 0xf bytes  C++
Qt5Sqld.dll!QSqlQueryPrivate::~QSqlQueryPrivate()  Line 94 + 0x23 bytes C++
Qt5Sqld.dll!QSqlQueryPrivate::`scalar deleting destructor'()  + 0xf bytes   C++
Qt5Sqld.dll!QSqlQuery::~QSqlQuery()  Line 245 + 0x1e bytes  C++
Qt5Sqld.dll!QSqlQueryModelPrivate::~QSqlQueryModelPrivate()  Line 90 + 0x3d bytes   C++
Qt5Sqld.dll!QSqlQueryModelPrivate::`scalar deleting destructor'()  + 0xf bytes  C++
Qt5Cored.dll!672cbf06()     
[Frames below may be incorrect and/or missing, no symbols loaded for Qt5Cored.dll]  
Qt5Cored.dll!672cb92a()     
Qt5Cored.dll!672c03f4()     
Qt5Cored.dll!67200dc4()     
Qt5Cored.dll!67203608()     
Qt5Sqld.dll!QSqlQueryModel::~QSqlQueryModel()  Line 175 + 0x9 bytes C++

As I mentioned above: the error doesn't happen when (one of below):

  • I create QSqlQueryModel without the parent (model = new QSqlQueryModel;)
  • I pass "static" query to QSqlQueryModel (regardless of having the parent).

eg:

model->setQuery(
    QSqlQuery(
        QString("SELECT \
            %1 as nazwa \
            , kontrahentid \
            FROM kontrahent"
        ).arg(showWhat)
    )
);

What am I doing wrong? And the real question is: what is the purpose for QSqlQueryModel having a parent? If I delete it manually in the window's destructor - is there any diffrence?

I guess this is a bug - I reported it on qt bugtracker: https://bugreports.qt.io/browse/QTBUG-43889

3
read about Qts parent/child concept here: qt-project.org/doc/qt-4.8/objecttrees.html ... to your code, did you delete the model in the destructor?Zaiborg
I read it... didn't find anything useful for my case. Yes, I call delete manually in the destructor (as mentioned in last sentence)murison
This is your problem. You can not delete model anywhere once you make it a child. The parent will delete it for you when the parent is freed.drescherjm
The error occured earlier - before I added manual delete in the destructor...murison
If that is the case are you using the correct Qt dlls for your compiler? Remember that you can not use Qt dlls compiled with some other version of Visual Studio (that will cause random looking heap corruption because you will have more than 1 independent heap). This is the reason the official downloads have binaries for more than 1 version of Visual Studio.drescherjm

3 Answers

1
votes

This is the key part:

if (model == NULL)
    //  model = new QSqlQueryModel; // app closes the window correctly
    model = new QSqlQueryModel(this); // app crashes when closing the window

The parent-child concept in Qt provides many features, with automatic child destruction being one of them.

If any QObject has other QObject set as a parent, then when parent QObject is deleted, the child object will also be deleted.

Now, you mentioned you do explicitly delete the model in destructor of the window. You should not. If you define this as a parent, then the model will be automatically deleted together with this, but it was already deleted by you in the destructor, so model is deleted twice, thus your error appears.

0
votes

So I removed the "delete operation" code in the destructor. Error still occurs, the only difference is diffrent call stack. error message:

Unhandled exception at 0x0f249f9a (qsqlpsqld.dll) in HurBudClientGUI.exe: 0xC0000005: Access violation reading location 0x00000004.

call stack:

qsqlpsqld.dll!QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> >::data()  Line 143 + 0x3 bytes C++
qsqlpsqld.dll!qGetPtrHelper<QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> > >(const QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> > & p)  Line 919 + 0xb bytes  C++
qsqlpsqld.dll!QPSQLDriver::d_func()  Line 106 + 0x13 bytes  C++
qsqlpsqld.dll!QPSQLResultPrivate::privDriver()  Line 212    C++
qsqlpsqld.dll!QPSQLResultPrivate::deallocatePreparedStmt()  Line 306 + 0xc bytes    C++
qsqlpsqld.dll!QPSQLResult::~QPSQLResult()  Line 328 C++
qsqlpsqld.dll!QPSQLResult::`scalar deleting destructor'()  + 0xf bytes  C++
Qt5Sqld.dll!QSqlQueryPrivate::~QSqlQueryPrivate()  Line 94 + 0x23 bytes C++
Qt5Sqld.dll!QSqlQueryPrivate::`scalar deleting destructor'()  + 0xf bytes   C++
Qt5Sqld.dll!QSqlQuery::~QSqlQuery()  Line 245 + 0x1e bytes  C++
Qt5Sqld.dll!QSqlQueryModelPrivate::~QSqlQueryModelPrivate()  Line 90 + 0x3d bytes   C++
Qt5Sqld.dll!QSqlQueryModelPrivate::`scalar deleting destructor'()  + 0xf bytes  C++
Qt5Cored.dll!QScopedPointerDeleter<QObjectData>::cleanup(QObjectData * pointer)  Line 62 + 0x20 bytes   C++
Qt5Cored.dll!QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> >::~QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> >()  Line 109 + 0x9 bytes  C++
Qt5Cored.dll!QObject::~QObject()  Line 940 + 0x15 bytes C++
Qt5Cored.dll!QAbstractItemModel::~QAbstractItemModel()  Line 1454 + 0xf bytes   C++
Qt5Cored.dll!QAbstractTableModel::~QAbstractTableModel()  Line 3299 + 0x8 bytes C++
Qt5Sqld.dll!QSqlQueryModel::~QSqlQueryModel()  Line 175 + 0x9 bytes C++
HurBudClientGUI.exe!QSqlQueryModel::`scalar deleting destructor'()  + 0x10 bytes    C++
Qt5Cored.dll!QObjectPrivate::deleteChildren()  Line 1841 + 0x24 bytes   C++
Qt5Widgetsd.dll!QWidget::~QWidget()  Line 1488  C++
Qt5Widgetsd.dll!QDockWidget::~QDockWidget()  Line 1172 + 0x22 bytes C++
HurBudClientGUI.exe!searchDock::~searchDock()  Line 28 + 0x1c bytes C++

searchDock is the class I'm working with.

0
votes

I guess I solved it. When the model is initialized as view's child:

model = new QSqlQueryModel(this);

The problem is order of operations. When I close the connection and remove the database in the destructor of my class - the database gets disconnected and no further operations are possible. But after my class' destructor, destructors of its base classes get into action - one by another, as ordered by inheritance. When QObject::~QObject() is executed, it gets to the

QObjectPrivate::deleteChildren()

method. And then, it finally gets to delete the model. The model wants to free the resources - which means QSqlResult (QPSQLResult to be specific in that case). This it how it looks:

QPSQLResult::~QPSQLResult()
{
    Q_D(QPSQLResult);
    cleanup();
    if (d->preparedQueriesEnabled && !d->preparedStmtId.isNull())
        d->deallocatePreparedStmt();
};

So here Qt tries to deallocate preparedStatement - regardless of the fact, that the connection no longer exists:

void QPSQLResultPrivate::deallocatePreparedStmt()
{
    const QString stmt = QLatin1String("DEALLOCATE ") + preparedStmtId;
    PGresult *result = privDriver()->exec(stmt);
    if (PQresultStatus(result) != PGRES_COMMAND_OK)
        qWarning("Unable to free statement: %s", PQerrorMessage(privDriver()->connection));
    PQclear(result);
    preparedStmtId.clear();
};

So - to make it work properly, I wolud have to call

QSqlQueryModel::~QSqlQueryModel()

or

QSqlQueryModel::clear()

BEFORE closing the connection with the DB. I still think it's a bug.