QSqlTableModel derived class crash on setFilter/select/everything



  • Hello, I have a following problem:
    I need to create a DB of students and courses with some editable info like grades etc.

    So I created a class deriving from QSqlTableModel and implemented some of the required functions (data, setData, roleNames)
    The class implementation looks like this: (please read the comments)

    #include "sqldb.h"
    
    #include <QDebug>
    #include <QSqlRecord>
    #include <QSqlError>
    #include <QSqlTableModel>
    #include <QSqlQuery>
    
    
    static bool createTable() {
        if (QSqlDatabase::database().tables().contains(QStringLiteral("Students"))) {
            // We want to clear it at the moment
            QSqlQuery query;
            if (!query.exec("DROP TABLE 'Students'"))
                qFatal("Failed to drop table 'Students' %s", qPrintable(query.lastError().text()));
        }
        QSqlQuery query;
        if (!query.exec(
            "CREATE TABLE IF NOT EXISTS 'Students' ("
            "    'id' INTEGER AUTO_INCREMENT, "
            "    'name' TEXT NOT NULL,"
            "    'department' TEXT NOT NULL, "
            "    PRIMARY KEY(id)"
            ")")) {
            qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
            return false;
        }
        return true;
    }
    
    
    SqlDb::SqlDb(QObject *parent) : QSqlTableModel(parent) {
        if (createTable()) {
            insertStudent("Albert Einstein", "PPT");
            insertStudent("Ernest Hemingway", "PPT");
        }
    
        setEditStrategy(QSqlTableModel::OnFieldChange);
        setTable("Students");
    
        if(!select())
            qFatal("Students SELECT query failed %s", qPrintable(lastError().text()));
    }
    
    bool SqlDb::setData(const QModelIndex &item, const QVariant &value, int role) {
        if (item.isValid() && role == Qt::EditRole) {
            QSqlTableModel::setData(item, value, role);
            emit dataChanged(item, item);
            return true;
        }
        return false;
    }
    
    QVariant SqlDb::data(const QModelIndex &index, int role) const {
    
        QVariant value;
    
        if (index.isValid()) {
            if (role < Qt::UserRole) {
                value = QSqlTableModel::data(index, role);
            } else {
                int columnIdx = role - Qt::UserRole - 1;
                QModelIndex modelIndex = this->index(index.row(), columnIdx);
                value = QSqlTableModel::data(modelIndex, Qt::DisplayRole);
            }
        }
        return value;
    }
    
    QHash<int, QByteArray> SqlDb::roleNames() const {
        QHash<int, QByteArray> roles;
        for (int i = 0; i < this->record().count(); i++) {
            roles.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8());
        }
        return roles;
    }
    
    QString SqlDb::name() {
        return n_name;
    }
    
    void SqlDb::setName(const QString &newName) {
        qDebug() << "I'mt the last printed line";
        if(newName == n_name)
            return;
    
        n_name = newName;
    
        
        const QString filterString = QString::fromLatin1("name = '%1'").arg(n_name);
        setFilter(filterString);
        // The program crashes here (Segmentation Fault)
        // after launching valgrind I got info "pure virtual function called"
        // but I have no idea which one that could be.
        // What's more if I commented out this `setFilter` then it
        // would crash on the `select()`
        qDebug() << "I'm not ever printed";
        if (!select())
            qDebug() << "Couldn't select with filter: %s" << lastError().text();
    
        emit nameChanged();
    }
    
    void SqlDb::insertStudent(const QString &name, const QString &department) {
        // The program doesn't also work when reusing the same DB and not using
        // this function at all
        qDebug() << "Inserting new student" << name << ", " << department;
    
        QString queryString = "INSERT INTO Students (name, department)"
                              "VALUES (?, ?)";
        QSqlQuery query(queryString);
        query.addBindValue(name);
        query.addBindValue(department);
    
        if (!query.exec())
            qDebug() << "Couldn't add new student" << query.lastError().text();
    }
    
    
    void SqlDb::updateStudentDetails(const QString &name, const QString &department) {
        // This function is not ever called now but it still doesn't work
        QSqlQuery query;
        query.prepare("UPDATE 'Students'"
                      "SET name = :name, department = :department "
                      "WHERE name == '" + n_name + "'");
        query.bindValue(":name", name);
        query.bindValue(":department", department);
        if(!query.exec())
            qDebug() << "Couldn't add new student" << query.lastError().text();
    }
    
    void SqlDb::refresh() {
        // This one also doesn't work - when called - SEGFAULT
        // also `clear()`  crashes
        QSqlQuery query;
        query.exec("SELECT * FROM Students");
        setQuery(query);
    }
    
    

    Well I guess that there is a fundamental bug somewhere in my code but I don't really know where.
    The header of the class:

    #ifndef SQLDB_H
    #define SQLDB_H
    
    #include <QSqlTableModel>
    
    
    class SqlDb: public QSqlTableModel {
        Q_OBJECT
        Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
    
    public:
        SqlDb(QObject *parent = 0);
    
        QVariant data(const QModelIndex &item, int role) const Q_DECL_OVERRIDE;
        QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
    
        bool setData(const QModelIndex &item, const QVariant &value, int role);
    
        QString name();
    
        Q_INVOKABLE void insertStudent(const QString &name, const QString &department);
        Q_INVOKABLE void updateStudentDetails(const QString &name, const QString &department);
    
    public slots:
        void refresh();
        void setName(const QString &newName);
    signals:
        void nameChanged();
    
    private:
        QString n_name;
    
    };
    
    #endif // SQLDB_H
    
    


    1. post the stack trace of the crash
    2. why are you reimplementing the whole model?


  • Well I don't have the stack trace, that's one of the problems. QtCreator doesn't even tell me that is SEGFAULT, to get that message I needed to run valgrind. <- I have the trace now, see below.

    And also I'm not really sure if I need to reimplement the whole model but I was learning from Qt Quick Chat Tutorial in which creating models was implemented this way.

    I believe I could just invoke some method on QSqlTableModel itself but would it have everything I need?
    And what I need is:

    • listing every row (surely ok)
    • setting filters to select single row by given name (student details) (probably ok, there is the setFilters function)
    • resetting filters to get the whole list back again (when user closes the details page)

    I'm a newbie and I'm really overhelmed by the amount of ways stuff can be done in Qt.

    Since I'm already here, I'd like to ask a question that is pretty close to the subject:
    if I have a model (let's say that it's QSqlTableModel) then is it better to instantiate it in every QML or create one object in main.cpp and pass it's reference to the view and then use it in all of the views?

    EDIT
    I found the stack-trace:

    1   ??                                                                                                                        0x2659650      
    2   ??                                                                                                                        
    3   ??                                                                                                                        
    4   QQuickItemView::minYExtent() const                                                                                        
    5   ??                                                                                                                        
    6   QQuickItemView::maxYExtent() const                                                                                        
    7   ??                                                                                                                         
    8   QQuickFlickable::setContentHeight(double)                                                                                 
    9   ??                                                                                                                         
    10  ??                                                                                                                         
    11  ??                                                                                                                        
    12  QQmlBinding::update(QFlags<QQmlPropertyData::WriteFlag>)                                                                  
    13  QQmlBinding::refresh()                                                                                                     
    14  QQmlNotifier::emitNotify(QQmlNotifierEndpoint *, void * *)                                                                 
    15  QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *, int, void * *)                                             
    16  QMetaObject::activate(QObject *, int, int, void * *)                                                                       
    17  QMetaObject::activate(QObject *, int, int, void * *)                                                                       
    18  QQuickItemPrivate::removeChild(QQuickItem *)                                                                              
    19  QQuickItem::setParentItem(QQuickItem *)                                                                                    
    20  QQuickItemView::destroyingItem(QObject *)                                                                                  
    21  ??                                                                                                                        
    22  QMetaObject::activate(QObject *, int, int, void * *)                                                                      
    23  QQmlInstanceModel::destroyingItem(QObject *)                                                                              
    24  ??                                                                                                                        
    25  QQmlDelegateModel::release(QObject *)                                                                                      
    26  ??                                                                                                                         
    27  ??                                                                                                                        
    28  ??                                                                                                                        
    29  ??                                                                                                                        
    30  ??                                                                                                                        
    31  QQuickItemView::modelUpdated(QQmlChangeSet const&, bool)                                                                   
    32  ??                                                                                                                         
    33  QMetaObject::activate(QObject *, int, int, void * *)                                                                       
    34  QQmlInstanceModel::modelUpdated(QQmlChangeSet const&, bool)                                                                
    35  ??                                                                                                                        
    36  ??                                                                                                                        
    37  ??                                                                                                                        
    38  QQmlDelegateModel::_q_modelReset()                                                                                        
    39  ??                                                                                                                        
    40  QQmlDelegateModel::qt_metacall(QMetaObject::Call, int, void * *)                                                          
    41  QMetaObject::activate(QObject *, int, int, void * *)                                                                      
    42  QAbstractItemModel::modelReset(QAbstractItemModel::QPrivateSignal)                                                        
    43  QAbstractItemModel::endResetModel()                                                                                       
    44  QSqlTableModel::select()                                                                                                  
    45  SqlDb::setName                                                                                          sqldb.cpp     110 0x407261       
    46  SqlDb::qt_static_metacall                                                                               moc_sqldb.cpp 104 0x4081d8       
    47  SqlDb::qt_metacall                                                                                      moc_sqldb.cpp 168 0x40842b       
    

    (i truncated the bottom since it works and also removed the addresses)
    45 is my call of setName, exactly the line with setFilter.
    Above that on the stack is the select function which also is broken, so that's why the setFilter doesn't work.

    Btw the funny thing is that in the constructor I also call select but it works in there.



  • Okay, I solved the problem.

    It was not a bug in the model I presented but in the QML (I found that out after reading the top of the stack - there are some functions that have more common with QML than SQL). SO I created a new QML, much simpler that would run some filters on the DB and yield the results.

    And it works.
    Blah, whole week wasted.

    Now I need to find out why my QML causes the app to crash but that's a thing for another thread.


Log in to reply
 

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