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


  • Lifetime Qt Champion

    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 ?



  • Hi,
    try this in the destructor:
    @
    yourDatabase = QSqlDatabase();
    QSqlDatabase::removeDatabase(yourDatabase.connectionName());
    @



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


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.