QSqlQueryModel & QSqlQuery::prepare - app crash
-
Hi,
I'm using Qt 5.2.1, Qt Visual Studio Addin 1.2.2 , Visual Studio 2010, Win7. I work with PostgreSQL database @QSqlDatabase::addDatabase("QPSQL");@
I have a problem using QSqlQueryModel with a QSqlQuery prepared by QSqlQuery::prepare() and QSqlQuery::exec(). I also described it on "stackoverflow":http://stackoverflow.com/questions/25660089/qsqlquerymodel-with-a-parent-app-crash
Here's the problem: I have a class:
@class searchDock : public QDockWidget
{
Q_OBJECT
(...)
QSqlQueryModel* model;
}@I initialize the model like this:
@model = new QSqlQueryModel(this);
model->setObjectName("model"); //so that QMetaObject::connectSlotsByName would work for the model's signals@Then - the model uses QSqlQuery query, that I prepare with a
QSqlQuery::prepare
method. I execute the query, and pass it to the model :
@query.prepare(...);
query.addBindValue(searchString);if (!query.exec()) {
qDebug() << query.lastError();
return;
}
model->setQuery(query);@And then I pass he model to a TableView:
@ui.kontrahenciList->setModel(model);
ui.kontrahenciList->show();@It all works as far. The problem is, that when I close the application, I get an error:
bq. Unhandled exception at 0x0f0c9f9a (qsqlpsqld.dll) in HurBudClientGUI.exe: 0xC0000005: Access violation reading location 0x00000004.
I do not delete anything manually anywhere .
I wanted to paste a StackTrace here, but I can't (I get an error, that this post is spam). You can see the stacktrace on my stackoverflow question.The problem doesn't occur when I use "plain" QSqlQuery (without prepared statement). The problem also occurs, when I initialize the model without a parent, but delete it in the searchDock desctructor.
So I have to initialize the model without a parent, and not delete it in my class' destructor - which leads to a memory leak.
Did I do something wrong? I guess it may be a bug - I reported it (QTBUG-43889) , but nobody replied.I sometimes get the same error when using QSqlTableModel and QSqlRelationalTableModel , but it doesn't happen every time...
Does anybody have an idea how to fix it? I upgraded Qt to current 5.4 version, but everything stayed the same - error still occurs... -
Hi and welcome to devnet,
It would be even better if you could attach a minimal compilable example to your bug report, if possible with a minimal SQL dump file to recreate the database so it's possible to try and reproduce your crash more easily
-
I found out, that the error is triggered when I have this line:
@QSqlDatabase::removeDatabase(QSqlDatabase::database().connectionName());@in my MainWindow::~MainWindow (destructor). Executing this line writes this:
bq. QSqlDatabasePrivate::removeDatabase: connection 'qt_sql_default_connection' is still in use, all queries will cease to work.
And next - the error is triggered. I heve just posted the code example to the bugreport (QTBUG-43889) .
Is it sufficient to just call
@QSqlDatabase::database().close();@to properly free the connection? Maybe I don't need to call QSqlDatabase::removeDatabase ?
-
[quote author="Clochydd" date="1422090466"]Hi,
try this in the destructor:
@
yourDatabase = QSqlDatabase();
QSqlDatabase::removeDatabase(yourDatabase.connectionName());
@
[/quote]Tried it - no effect:
@dbTestGUI::~dbTestGUI()
{
QSqlDatabase db = QSqlDatabase::database();
//db.close();
QSqlDatabase::removeDatabase(db.connectionName()); //this line causes error
}@ -
Hi,
I always open my databases that way:
@
db = QSqlDatabase::addDatabase("QPSQL");
db.setDatabaseName("myDB");
db.setHostName("localhost");
db.setPort(5432);
if (!db.open("user", "password")) {
...
}
...
@and close it the destructor:
@
db = QSqlDatabase();
QSqlDatabase::removeDatabase(db.connectionName());
@ -
According to the doc:
bq. QSqlDatabase::QSqlDatabase()
Creates an empty, invalid QSqlDatabase object. Use addDatabase(), removeDatabase(), and database() to get valid QSqlDatabase objects.So I guess that you remove an invalid database - which probably doesn't do anything at all...
-
As i thought - when using your code - nothing happens. Neither is the connection closed (I can still see it it PgAdmin's "server status window":http://www.pgadmin.org/docs/dev/status.html), nor are the dll's unloaded. So I guess - that you'have been always closing it the wrong way :)
-
Just checked with pgAdmin's server status:
The connection appears when any of my postgres based programs is opened and disappears when the program is closed...I came to the above described solution after several weeks of trial and error and with support by this forum.
-
Well, yes. The connection is created when you call QSqlDatabase::open. And it is closed as the application is closed - even if you don't call anything in the destructor. What I meant, is that if you execute and debug your code line-by-line - it does nothing.
Consider the following:
@dbTestGUI::~dbTestGUI()
{
QSqlDatabase db = QSqlDatabase();
qDebug() << db.isValid(); //returns false
db.close(); //does nothing - connection still visible in pgadmin server status
QSqlDatabase::removeDatabase(db.connectionName()); does nothig - no effect visible
}@versus:
@dbTestGUI::~dbTestGUI()
{
QSqlDatabase db = QSqlDatabase().database();
qDebug() << db.isValid(); //returns true
db.close(); //connection disappears from server status
QSqlDatabase::removeDatabase(db.connectionName()); // sometimes some DLL's get unloaded, sometimest not.
//the following line appears on the console: "QSqlDatabasePrivate::removeDatabase: connection ‘qt_sql_default_connection’ is still in use, all queries will cease to work."
}@When run the app, after executing this destructor it crashes. But when i comment the line:
@QSqlDatabase::removeDatabase(db.connectionName()); @it doesn't
-
So, to sum up - this is my destructor now:
@dbTestGUI::~dbTestGUI()
{
QSqlDatabase db = QSqlDatabase().database();
qDebug() << db.isValid();
db.close();
//QSqlDatabase::removeDatabase(db.connectionName()); //this line results in error after executing the d-tor
}@
when stepping out of it, I get the following message:bq. Unable to free statement: connection pointer is NULL
So i guess, that Qt has a general problem with free'ing Prepared Statemets. Probably, because on one hand the QSqlQuery object is copied to the model (via QSqlQueryModel::setQuery), ant on the other hand QSqlQuery's parent is QSqlDatabase:
@QSqlQuery::QSqlQuery(const QString & query = QString(), QSqlDatabase db = QSqlDatabase()) @So instead of deallocating the query when the model is deleted - it is dealocated, when the database object gets destroyed.
-
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.