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
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.
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.
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.