Skip to content

Commit

Permalink
Fix QgsQueryResultModel crash
Browse files Browse the repository at this point in the history
  • Loading branch information
elpaso committed Jan 15, 2021
1 parent 79572de commit 2d79916
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 16 deletions.
9 changes: 8 additions & 1 deletion python/core/auto_generated/qgsqueryresultmodel.sip.in
Expand Up @@ -13,7 +13,7 @@



class QgsQueryResultModel : QAbstractListModel
class QgsQueryResultModel : QAbstractTableModel
{
%Docstring
The QgsQueryResultModel class is a model for QgsAbstractDatabaseProviderConnection.QueryResult
Expand Down Expand Up @@ -41,12 +41,19 @@ Constructs a QgsQueryResultModel from a ``queryResult`` with optional ``parent``

virtual QVariant data( const QModelIndex &index, int role ) const;

virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const;


public slots:

void rowsReady( const QList<QList<QVariant> > &rows );
%Docstring
Triggered when ``newRows`` have been fetched and can be added to the model
%End

void cancel();
%Docstring
Cancels the row fetching.
%End

};
Expand Down
35 changes: 28 additions & 7 deletions src/core/qgsqueryresultmodel.cpp
Expand Up @@ -16,7 +16,7 @@
#include "qgsqueryresultmodel.h"

QgsQueryResultModel::QgsQueryResultModel( const QgsAbstractDatabaseProviderConnection::QueryResult &queryResult, QObject *parent )
: QAbstractListModel( parent )
: QAbstractTableModel( parent )
, mQueryResult( queryResult )
, mColumns( queryResult.columns() )
{
Expand All @@ -33,11 +33,19 @@ QgsQueryResultModel::QgsQueryResultModel( const QgsAbstractDatabaseProviderConne

void QgsQueryResultModel::rowsReady( const QList<QList<QVariant>> &rows )
{
beginInsertRows( QModelIndex(), rows.count(), mRows.count( ) + rows.count() - 1 );
beginInsertRows( QModelIndex(), mRows.count( ), mRows.count( ) + rows.count() - 1 );
mRows.append( rows );
endInsertRows();
}

void QgsQueryResultModel::cancel()
{
if ( mWorker )
{
mWorker->stopFetching();
}
}

QgsQueryResultModel::~QgsQueryResultModel()
{
if ( mWorker )
Expand Down Expand Up @@ -65,21 +73,34 @@ int QgsQueryResultModel::columnCount( const QModelIndex &parent ) const

QVariant QgsQueryResultModel::data( const QModelIndex &index, int role ) const
{
if ( !index.isValid() || index.row() < 0 || index.column() > mColumns.count() - 1 ||
if ( !index.isValid() || index.row() < 0 || index.column() >= mColumns.count() ||
index.row() >= mRows.count( ) )
return QVariant();

if ( role == Qt::DisplayRole )
switch ( role )
{
const QList<QVariant> result { mRows.at( index.row() ) };
if ( index.column() < result.count( ) )
case Qt::DisplayRole:
{
return result.at( index.column() );
const QList<QVariant> result { mRows.at( index.row() ) };
if ( index.column() < result.count( ) )
{
return result.at( index.column() );
}
break;
}
}
return QVariant();
}

QVariant QgsQueryResultModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
if ( orientation == Qt::Orientation::Horizontal && role == Qt::ItemDataRole::DisplayRole && section < mColumns.count() )
{
return mColumns.at( section );
}
return QAbstractTableModel::headerData( section, orientation, role );
}

///@cond private

const int QgsQueryResultFetcher::ROWS_TO_FETCH = 200;
Expand Down
12 changes: 9 additions & 3 deletions src/core/qgsqueryresultmodel.h
Expand Up @@ -16,7 +16,7 @@
#ifndef QgsQueryResultModel_H
#define QgsQueryResultModel_H

#include <QAbstractListModel>
#include <QAbstractTableModel>
#include <QThread>

#include "qgis_core.h"
Expand Down Expand Up @@ -73,7 +73,7 @@ class QgsQueryResultFetcher: public QObject
* \ingroup core
* \since QGIS 3.18
*/
class CORE_EXPORT QgsQueryResultModel : public QAbstractListModel
class CORE_EXPORT QgsQueryResultModel : public QAbstractTableModel
{
Q_OBJECT
public:
Expand All @@ -91,6 +91,7 @@ class CORE_EXPORT QgsQueryResultModel : public QAbstractListModel
int rowCount( const QModelIndex &parent ) const override;
int columnCount( const QModelIndex &parent ) const override;
QVariant data( const QModelIndex &index, int role ) const override;
QVariant headerData( int section, Qt::Orientation orientation, int role ) const override;

public slots:

Expand All @@ -99,9 +100,14 @@ class CORE_EXPORT QgsQueryResultModel : public QAbstractListModel
*/
void rowsReady( const QList<QList<QVariant> > &rows );

/**
* Cancels the row fetching.
*/
void cancel();

private:

const QgsAbstractDatabaseProviderConnection::QueryResult &mQueryResult;
QgsAbstractDatabaseProviderConnection::QueryResult mQueryResult;
QStringList mColumns;
QThread mWorkerThread;
QgsQueryResultFetcher *mWorker = nullptr;
Expand Down
17 changes: 12 additions & 5 deletions tests/src/python/test_qgsqueryresultmodel.py
Expand Up @@ -44,6 +44,8 @@ def setUpClass(cls):

# Prepare data for threaded test
cls._deleteBigData()

return
md = QgsProviderRegistry.instance().providerMetadata('postgres')
conn = md.createConnection(cls.uri, {})
conn.executeSql('SELECT * INTO qgis_test.random_big_data FROM generate_series(1,%s) AS id, md5(random()::text) AS descr' % cls.NUM_RECORDS)
Expand All @@ -56,6 +58,7 @@ def tearDownClass(cls):
@classmethod
def _deleteBigData(cls):

return
try:
md = QgsProviderRegistry.instance().providerMetadata('postgres')
conn = md.createConnection(cls.uri, {})
Expand All @@ -70,13 +73,13 @@ def test_model(self):
conn = md.createConnection(self.uri, {})
res = conn.execSql('SELECT generate_series(1, 1000)')
model = QgsQueryResultModel(res)
self.assertEqual(model.rowCount(model.index(-1)), 0)
self.assertEqual(model.rowCount(model.index(-1, -1)), 0)

while model.rowCount(model.index(-1)) < 1000:
while model.rowCount(model.index(-1, -1)) < 1000:
QCoreApplication.processEvents()

self.assertEqual(model.columnCount(model.index(-1)), 1)
self.assertEqual(model.rowCount(model.index(-1)), 1000)
self.assertEqual(model.columnCount(model.index(-1, -1)), 1)
self.assertEqual(model.rowCount(model.index(-1, -1)), 1000)
self.assertEqual(model.data(model.index(999, 0), Qt.DisplayRole), 1000)

# Test data
Expand Down Expand Up @@ -130,12 +133,16 @@ def test_widget(self):
v.setModel(model)

def _set_row_count(idx, first, last):
lbl.setText('Rows %s fetched' % model.rowCount(model.index(-1)))
lbl.setText('Rows %s fetched' % model.rowCount(model.index(-1, -1)))

model.rowsInserted.connect(_set_row_count)

d.exec_()

# Because exit handler will exit QGIS and clear the connections pool before
# the model is deleted (and it will in turn clear the connection)
del(model)


if __name__ == '__main__':
unittest.main()

0 comments on commit 2d79916

Please sign in to comment.