/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "../../kernel/qsqldatabase/tst_databases.h" Q_DECLARE_METATYPE(Qt::Orientation) class tst_QSqlQueryModel : public QObject { Q_OBJECT public: tst_QSqlQueryModel(); virtual ~tst_QSqlQueryModel(); public slots: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); private slots: void insertColumn_data() { generic_data(); } void insertColumn(); void removeColumn_data() { generic_data(); } void removeColumn(); void record_data() { generic_data(); } void record(); void setHeaderData_data() { generic_data(); } void setHeaderData(); void fetchMore_data() { generic_data(); } void fetchMore(); //problem specific tests void withSortFilterProxyModel_data() { generic_data(); } void withSortFilterProxyModel(); void setQuerySignalEmission_data() { generic_data(); } void setQuerySignalEmission(); void setQueryWithNoRowsInResultSet_data() { generic_data(); } void setQueryWithNoRowsInResultSet(); void task_180617(); void task_180617_data() { generic_data(); } void task_QTBUG_4963_setHeaderDataWithProxyModel(); private: void generic_data(const QString &engine=QString()); void dropTestTables(QSqlDatabase db); void createTestTables(QSqlDatabase db); void populateTestTables(QSqlDatabase db); tst_Databases dbs; }; /* Stupid class that makes protected members public for testing */ class DBTestModel: public QSqlQueryModel { public: DBTestModel(QObject *parent = 0): QSqlQueryModel(parent) {} QModelIndex indexInQuery(const QModelIndex &item) const { return QSqlQueryModel::indexInQuery(item); } }; tst_QSqlQueryModel::tst_QSqlQueryModel() { } tst_QSqlQueryModel::~tst_QSqlQueryModel() { } void tst_QSqlQueryModel::initTestCase() { qRegisterMetaType("QModelIndex"); dbs.open(); for (QStringList::ConstIterator it = dbs.dbNames.begin(); it != dbs.dbNames.end(); ++it) { QSqlDatabase db = QSqlDatabase::database((*it)); CHECK_DATABASE(db); dropTestTables(db); //in case of leftovers createTestTables(db); populateTestTables(db); } } void tst_QSqlQueryModel::cleanupTestCase() { for (QStringList::ConstIterator it = dbs.dbNames.begin(); it != dbs.dbNames.end(); ++it) { QSqlDatabase db = QSqlDatabase::database((*it)); CHECK_DATABASE(db); dropTestTables(db); } dbs.close(); } void tst_QSqlQueryModel::dropTestTables(QSqlDatabase db) { QStringList tableNames; tableNames << qTableName("test", __FILE__) << qTableName("test2", __FILE__) << qTableName("test3", __FILE__) << qTableName("many", __FILE__); tst_Databases::safeDropTables(db, tableNames); } void tst_QSqlQueryModel::createTestTables(QSqlDatabase db) { dropTestTables(db); QSqlQuery q(db); if(tst_Databases::isPostgreSQL(db)) QVERIFY_SQL( q, exec("set client_min_messages='warning'")); QVERIFY_SQL( q, exec("create table " + qTableName("test", __FILE__) + "(id integer not null, name varchar(20), title integer, primary key (id))")); QVERIFY_SQL( q, exec("create table " + qTableName("test2", __FILE__) + "(id integer not null, title varchar(20), primary key (id))")); QVERIFY_SQL( q, exec("create table " + qTableName("test3", __FILE__) + "(id integer not null, primary key (id))")); QVERIFY_SQL( q, exec("create table " + qTableName("many", __FILE__) + "(id integer not null, name varchar(20), primary key (id))")); } void tst_QSqlQueryModel::populateTestTables(QSqlDatabase db) { qWarning() << "Populating test tables, this can take quite a while... ZZZzzz..."; bool hasTransactions = db.driver()->hasFeature(QSqlDriver::Transactions); QSqlQuery q(db), q2(db); tst_Databases::safeDropTables(db, QStringList() << qTableName("manytmp", __FILE__) << qTableName("test3tmp", __FILE__)); QVERIFY_SQL(q, exec("create table " + qTableName("manytmp", __FILE__) + "(id integer not null, name varchar(20), primary key (id))")); QVERIFY_SQL(q, exec("create table " + qTableName("test3tmp", __FILE__) + "(id integer not null, primary key (id))")); if (hasTransactions) QVERIFY_SQL(db, transaction()); QVERIFY_SQL(q, exec("insert into " + qTableName("test", __FILE__) + " values(1, 'harry', 1)")); QVERIFY_SQL(q, exec("insert into " + qTableName("test", __FILE__) + " values(2, 'trond', 2)")); QVERIFY_SQL(q, exec("insert into " + qTableName("test2", __FILE__) + " values(1, 'herr')")); QVERIFY_SQL(q, exec("insert into " + qTableName("test2", __FILE__) + " values(2, 'mister')")); QVERIFY_SQL(q, exec(QString("insert into " + qTableName("test3", __FILE__) + " values(0)"))); QVERIFY_SQL(q, prepare("insert into "+qTableName("test3", __FILE__)+"(id) select id + ? from "+qTableName("test3tmp", __FILE__))); for (int i=1; i<260; i*=2) { q2.exec("delete from "+qTableName("test3tmp", __FILE__)); QVERIFY_SQL(q2, exec("insert into "+qTableName("test3tmp", __FILE__)+"(id) select id from "+qTableName("test3", __FILE__))); q.bindValue(0, i); QVERIFY_SQL(q, exec()); } QVERIFY_SQL(q, exec(QString("insert into " + qTableName("many", __FILE__) + "(id, name) values (0, \'harry\')"))); QVERIFY_SQL(q, prepare("insert into "+qTableName("many", __FILE__)+"(id, name) select id + ?, name from "+qTableName("manytmp", __FILE__))); for (int i=1; i < 2048; i*=2) { q2.exec("delete from "+qTableName("manytmp", __FILE__)); QVERIFY_SQL(q2, exec("insert into "+qTableName("manytmp", __FILE__)+"(id, name) select id, name from "+qTableName("many", __FILE__))); q.bindValue(0, i); QVERIFY_SQL(q, exec()); } if (hasTransactions) QVERIFY_SQL(db, commit()); tst_Databases::safeDropTables(db, QStringList() << qTableName("manytmp", __FILE__) << qTableName("test3tmp", __FILE__)); } void tst_QSqlQueryModel::generic_data(const QString& engine) { if ( dbs.fillTestTable(engine) == 0 ) { if(engine.isEmpty()) QSKIP( "No database drivers are available in this Qt configuration"); else QSKIP( (QString("No database drivers of type %1 are available in this Qt configuration").arg(engine)).toLocal8Bit()); } } void tst_QSqlQueryModel::init() { } void tst_QSqlQueryModel::cleanup() { } void tst_QSqlQueryModel::removeColumn() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); DBTestModel model; model.setQuery(QSqlQuery("select * from " + qTableName("test", __FILE__), db)); model.fetchMore(); QSignalSpy spy(&model, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int))); QCOMPARE(model.columnCount(), 3); QVERIFY(model.removeColumn(0)); QCOMPARE(spy.count(), 1); QVERIFY(*(QModelIndex *)spy.at(0).at(0).constData() == QModelIndex()); QCOMPARE(spy.at(0).at(1).toInt(), 0); QCOMPARE(spy.at(0).at(2).toInt(), 0); QCOMPARE(model.columnCount(), 2); QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 1); QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), 2); QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1); QVERIFY(model.insertColumn(1)); QCOMPARE(model.columnCount(), 3); QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 1); QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), 2); QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1); QCOMPARE(model.data(model.index(0, 0)).toString(), QString("harry")); QCOMPARE(model.data(model.index(0, 1)), QVariant()); QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); QCOMPARE(model.data(model.index(0, 3)), QVariant()); QVERIFY(!model.removeColumn(42)); QVERIFY(!model.removeColumn(3)); QVERIFY(!model.removeColumn(1, model.index(1, 2))); QCOMPARE(model.columnCount(), 3); QVERIFY(model.removeColumn(2)); QCOMPARE(spy.count(), 2); QVERIFY(*(QModelIndex *)spy.at(1).at(0).constData() == QModelIndex()); QCOMPARE(spy.at(1).at(1).toInt(), 2); QCOMPARE(spy.at(1).at(2).toInt(), 2); QCOMPARE(model.columnCount(), 2); QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 1); QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1); QVERIFY(model.removeColumn(1)); QCOMPARE(spy.count(), 3); QVERIFY(*(QModelIndex *)spy.at(2).at(0).constData() == QModelIndex()); QCOMPARE(spy.at(2).at(1).toInt(), 1); QCOMPARE(spy.at(2).at(2).toInt(), 1); QCOMPARE(model.columnCount(), 1); QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 1); QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1); QCOMPARE(model.data(model.index(0, 0)).toString(), QString("harry")); QVERIFY(model.removeColumn(0)); QCOMPARE(spy.count(), 4); QVERIFY(*(QModelIndex *)spy.at(3).at(0).constData() == QModelIndex()); QCOMPARE(spy.at(3).at(1).toInt(), 0); QCOMPARE(spy.at(3).at(2).toInt(), 0); QCOMPARE(model.columnCount(), 0); QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1); } void tst_QSqlQueryModel::insertColumn() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); DBTestModel model; model.setQuery(QSqlQuery("select * from " + qTableName("test", __FILE__), db)); model.fetchMore(); // necessary??? bool isToUpper = db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2"); const QString idColumn(isToUpper ? "ID" : "id"); const QString nameColumn(isToUpper ? "NAME" : "name"); const QString titleColumn(isToUpper ? "TITLE" : "title"); QSignalSpy spy(&model, SIGNAL(columnsInserted(QModelIndex, int, int))); QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); QCOMPARE(model.data(model.index(0, 3)), QVariant()); QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), idColumn); QCOMPARE(model.headerData(1, Qt::Horizontal).toString(), nameColumn); QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), titleColumn); QCOMPARE(model.headerData(3, Qt::Horizontal).toString(), QString("4")); QVERIFY(model.insertColumn(1)); QCOMPARE(spy.count(), 1); QVERIFY(*(QModelIndex *)spy.at(0).at(0).constData() == QModelIndex()); QCOMPARE(spy.at(0).at(1).toInt(), 1); QCOMPARE(spy.at(0).at(2).toInt(), 1); QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 0); QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), 1); QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), 2); QCOMPARE(model.indexInQuery(model.index(0, 4)).column(), -1); QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); QCOMPARE(model.data(model.index(0, 1)), QVariant()); QCOMPARE(model.data(model.index(0, 2)).toString(), QString("harry")); QCOMPARE(model.data(model.index(0, 3)).toInt(), 1); QCOMPARE(model.data(model.index(0, 4)), QVariant()); QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), idColumn); QCOMPARE(model.headerData(1, Qt::Horizontal).toString(), QString("2")); QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), nameColumn); QCOMPARE(model.headerData(3, Qt::Horizontal).toString(), titleColumn); QCOMPARE(model.headerData(4, Qt::Horizontal).toString(), QString("5")); QVERIFY(!model.insertColumn(-1)); QVERIFY(!model.insertColumn(100)); QVERIFY(!model.insertColumn(1, model.index(1, 1))); QVERIFY(model.insertColumn(0)); QCOMPARE(spy.count(), 2); QVERIFY(*(QModelIndex *)spy.at(1).at(0).constData() == QModelIndex()); QCOMPARE(spy.at(1).at(1).toInt(), 0); QCOMPARE(spy.at(1).at(2).toInt(), 0); QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), 0); QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), 1); QCOMPARE(model.indexInQuery(model.index(0, 4)).column(), 2); QCOMPARE(model.indexInQuery(model.index(0, 5)).column(), -1); QVERIFY(!model.insertColumn(6)); QVERIFY(model.insertColumn(5)); QCOMPARE(spy.count(), 3); QVERIFY(*(QModelIndex *)spy.at(2).at(0).constData() == QModelIndex()); QCOMPARE(spy.at(2).at(1).toInt(), 5); QCOMPARE(spy.at(2).at(2).toInt(), 5); QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), 0); QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), 1); QCOMPARE(model.indexInQuery(model.index(0, 4)).column(), 2); QCOMPARE(model.indexInQuery(model.index(0, 5)).column(), -1); QCOMPARE(model.indexInQuery(model.index(0, 6)).column(), -1); QCOMPARE(model.record().field(0).name(), QString()); QCOMPARE(model.record().field(1).name(), idColumn); QCOMPARE(model.record().field(2).name(), QString()); QCOMPARE(model.record().field(3).name(), nameColumn); QCOMPARE(model.record().field(4).name(), titleColumn); QCOMPARE(model.record().field(5).name(), QString()); QCOMPARE(model.record().field(6).name(), QString()); QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("1")); QCOMPARE(model.headerData(1, Qt::Horizontal).toString(), idColumn); QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), QString("3")); QCOMPARE(model.headerData(3, Qt::Horizontal).toString(), nameColumn); QCOMPARE(model.headerData(4, Qt::Horizontal).toString(), titleColumn); QCOMPARE(model.headerData(5, Qt::Horizontal).toString(), QString("6")); QCOMPARE(model.headerData(6, Qt::Horizontal).toString(), QString("7")); } void tst_QSqlQueryModel::record() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); QSqlQueryModel model; model.setQuery(QSqlQuery("select * from " + qTableName("test", __FILE__), db)); QSqlRecord rec = model.record(); bool isToUpper = db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2"); QCOMPARE(rec.count(), 3); QCOMPARE(rec.fieldName(0), isToUpper ? QString("ID") : QString("id")); QCOMPARE(rec.fieldName(1), isToUpper ? QString("NAME") : QString("name")); QCOMPARE(rec.fieldName(2), isToUpper ? QString("TITLE") : QString("title")); QCOMPARE(rec.value(0), QVariant(rec.field(0).type())); QCOMPARE(rec.value(1), QVariant(rec.field(1).type())); QCOMPARE(rec.value(2), QVariant(rec.field(2).type())); rec = model.record(0); QCOMPARE(rec.fieldName(0), isToUpper ? QString("ID") : QString("id")); QCOMPARE(rec.fieldName(1), isToUpper ? QString("NAME") : QString("name")); QCOMPARE(rec.fieldName(2), isToUpper ? QString("TITLE") : QString("title")); QCOMPARE(rec.value(0).toString(), QString("1")); QCOMPARE(rec.value(1), QVariant("harry")); QCOMPARE(rec.value(2), QVariant(1)); } void tst_QSqlQueryModel::setHeaderData() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); QSqlQueryModel model; QVERIFY(!model.setHeaderData(5, Qt::Vertical, "foo")); QVERIFY(model.headerData(5, Qt::Vertical).isValid()); model.setQuery(QSqlQuery("select * from " + qTableName("test", __FILE__), db)); qRegisterMetaType("Qt::Orientation"); QSignalSpy spy(&model, SIGNAL(headerDataChanged(Qt::Orientation, int, int))); QVERIFY(model.setHeaderData(2, Qt::Horizontal, "bar")); QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), QString("bar")); QCOMPARE(spy.count(), 1); QCOMPARE(qvariant_cast(spy.value(0).value(0)), Qt::Horizontal); QCOMPARE(spy.value(0).value(1).toInt(), 2); QCOMPARE(spy.value(0).value(2).toInt(), 2); QVERIFY(!model.setHeaderData(7, Qt::Horizontal, "foo", Qt::ToolTipRole)); QVERIFY(!model.headerData(7, Qt::Horizontal, Qt::ToolTipRole).isValid()); bool isToUpper = db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2"); QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), isToUpper ? QString("ID") : QString("id")); QCOMPARE(model.headerData(1, Qt::Horizontal).toString(), isToUpper ? QString("NAME") : QString("name")); QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), QString("bar")); QVERIFY(model.headerData(3, Qt::Horizontal).isValid()); } void tst_QSqlQueryModel::fetchMore() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); QSqlQueryModel model; QSignalSpy spy(&model, SIGNAL(rowsInserted(QModelIndex, int, int))); model.setQuery(QSqlQuery("select * from " + qTableName("many", __FILE__), db)); int rowCount = model.rowCount(); QCOMPARE(spy.value(0).value(1).toInt(), 0); QCOMPARE(spy.value(0).value(2).toInt(), rowCount - 1); // If the driver doesn't return the query size fetchMore() causes the // model to grow and new signals are emitted if (!db.driver()->hasFeature(QSqlDriver::QuerySize)) { spy.clear(); model.fetchMore(); int newRowCount = model.rowCount(); QCOMPARE(spy.value(0).value(1).toInt(), rowCount); QCOMPARE(spy.value(0).value(2).toInt(), newRowCount - 1); } } // For task 149491: When used with QSortFilterProxyModel, a view and a // database that doesn't support the QuerySize feature, blank rows was // appended if the query returned more than 256 rows and setQuery() // was called more than once. This because an insertion of rows was // triggered at the same time as the model was being cleared. void tst_QSqlQueryModel::withSortFilterProxyModel() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); if (db.driver()->hasFeature(QSqlDriver::QuerySize)) QSKIP("Test applies only for drivers not reporting the query size."); QSqlQueryModel model; model.setQuery(QSqlQuery("SELECT * FROM " + qTableName("test3", __FILE__), db)); QSortFilterProxyModel proxy; proxy.setSourceModel(&model); QTableView view; view.setModel(&proxy); QSignalSpy modelRowsRemovedSpy(&model, SIGNAL(rowsRemoved(const QModelIndex &, int, int))); QSignalSpy modelRowsInsertedSpy(&model, SIGNAL(rowsInserted(const QModelIndex &, int, int))); model.setQuery(QSqlQuery("SELECT * FROM " + qTableName("test3", __FILE__), db)); view.scrollToBottom(); QTestEventLoop::instance().enterLoop(1); QCOMPARE(proxy.rowCount(), 511); // The second call to setQuery() clears the model by removing it's rows. // Only 256 rows because that is what was cached. QCOMPARE(modelRowsRemovedSpy.count(), 1); QCOMPARE(modelRowsRemovedSpy.value(0).value(1).toInt(), 0); QCOMPARE(modelRowsRemovedSpy.value(0).value(2).toInt(), 255); // The call to scrollToBottom() forces the model to fetch all rows, // which will be done in two steps. QCOMPARE(modelRowsInsertedSpy.count(), 2); QCOMPARE(modelRowsInsertedSpy.value(0).value(1).toInt(), 0); QCOMPARE(modelRowsInsertedSpy.value(0).value(2).toInt(), 255); QCOMPARE(modelRowsInsertedSpy.value(1).value(1).toInt(), 256); QCOMPARE(modelRowsInsertedSpy.value(1).value(2).toInt(), 510); } // For task 155402: When the model is already empty when setQuery() is called // no rows have to be removed and rowsAboutToBeRemoved and rowsRemoved should // not be emitted. void tst_QSqlQueryModel::setQuerySignalEmission() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); QSqlQueryModel model; QSignalSpy modelRowsAboutToBeRemovedSpy(&model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int))); QSignalSpy modelRowsRemovedSpy(&model, SIGNAL(rowsRemoved(const QModelIndex &, int, int))); // First select, the model was empty and no rows had to be removed! model.setQuery(QSqlQuery("SELECT * FROM " + qTableName("test", __FILE__), db)); QCOMPARE(modelRowsAboutToBeRemovedSpy.count(), 0); QCOMPARE(modelRowsRemovedSpy.count(), 0); // Second select, the model wasn't empty and two rows had to be removed! model.setQuery(QSqlQuery("SELECT * FROM " + qTableName("test", __FILE__), db)); QCOMPARE(modelRowsAboutToBeRemovedSpy.count(), 1); QCOMPARE(modelRowsAboutToBeRemovedSpy.value(0).value(1).toInt(), 0); QCOMPARE(modelRowsAboutToBeRemovedSpy.value(0).value(2).toInt(), 1); QCOMPARE(modelRowsRemovedSpy.count(), 1); QCOMPARE(modelRowsRemovedSpy.value(0).value(1).toInt(), 0); QCOMPARE(modelRowsRemovedSpy.value(0).value(2).toInt(), 1); } // For task 170783: When the query's result set is empty no rows should be inserted, // i.e. no rowsAboutToBeInserted or rowsInserted signals should be emitted. void tst_QSqlQueryModel::setQueryWithNoRowsInResultSet() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); QSqlQueryModel model; QSignalSpy modelRowsAboutToBeInsertedSpy(&model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int))); QSignalSpy modelRowsInsertedSpy(&model, SIGNAL(rowsInserted(const QModelIndex &, int, int))); // The query's result set will be empty so no signals should be emitted! QSqlQuery query(db); QVERIFY_SQL(query, exec("SELECT * FROM " + qTableName("test", __FILE__) + " where 0 = 1")); model.setQuery(query); QCOMPARE(modelRowsAboutToBeInsertedSpy.count(), 0); QCOMPARE(modelRowsInsertedSpy.count(), 0); } // For task 180617 // According to the task, several specific duplicate SQL queries would cause // multiple empty grid lines to be visible in the view void tst_QSqlQueryModel::task_180617() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); const QString test3(qTableName("test3", __FILE__)); QTableView view; QCOMPARE(view.columnAt(0), -1); QCOMPARE(view.rowAt(0), -1); QSqlQueryModel model; model.setQuery( "SELECT TOP 0 * FROM " + test3, db ); view.setModel(&model); bool error = false; // Usually a syntax error if (model.lastError().isValid()) // usually a syntax error error = true; QCOMPARE(view.columnAt(0), (error)?-1:0 ); QCOMPARE(view.rowAt(0), -1); model.setQuery( "SELECT TOP 0 * FROM " + test3, db ); model.setQuery( "SELECT TOP 0 * FROM " + test3, db ); model.setQuery( "SELECT TOP 0 * FROM " + test3, db ); model.setQuery( "SELECT TOP 0 * FROM " + test3, db ); QCOMPARE(view.columnAt(0), (error)?-1:0 ); QCOMPARE(view.rowAt(0), -1); } void tst_QSqlQueryModel::task_QTBUG_4963_setHeaderDataWithProxyModel() { QSqlQueryModel plainModel; QSortFilterProxyModel proxyModel; proxyModel.setSourceModel(&plainModel); QVERIFY(!plainModel.setHeaderData(0, Qt::Horizontal, QObject::tr("ID"))); // And it should not crash. } QTEST_MAIN(tst_QSqlQueryModel) #include "tst_qsqlquerymodel.moc"