1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <QtGui>
32#include <QtWidgets>
33
34#include <qsqldriver.h>
35#include <qsqldatabase.h>
36#include <qsqlerror.h>
37#include <qsqlfield.h>
38#include <qsqlquery.h>
39#include <qsqlrecord.h>
40
41#include <qsqlquerymodel.h>
42#include <qsortfilterproxymodel.h>
43
44#include "../../kernel/qsqldatabase/tst_databases.h"
45
46Q_DECLARE_METATYPE(Qt::Orientation)
47
48class tst_QSqlQueryModel : public QObject
49{
50 Q_OBJECT
51
52public:
53 tst_QSqlQueryModel();
54 virtual ~tst_QSqlQueryModel();
55
56public slots:
57 void initTestCase();
58 void cleanupTestCase();
59 void init();
60 void cleanup();
61
62private slots:
63 void insertColumn_data() { generic_data(); }
64 void insertColumn();
65 void removeColumn_data() { generic_data(); }
66 void removeColumn();
67 void record_data() { generic_data(); }
68 void record();
69 void setHeaderData_data() { generic_data(); }
70 void setHeaderData();
71 void fetchMore_data() { generic_data(); }
72 void fetchMore();
73
74 //problem specific tests
75 void withSortFilterProxyModel_data() { generic_data(); }
76 void withSortFilterProxyModel();
77 void setQuerySignalEmission_data() { generic_data(); }
78 void setQuerySignalEmission();
79 void setQueryWithNoRowsInResultSet_data() { generic_data(); }
80 void setQueryWithNoRowsInResultSet();
81 void nestedResets_data() { generic_data(); }
82 void nestedResets();
83
84 void task_180617();
85 void task_180617_data() { generic_data(); }
86 void task_QTBUG_4963_setHeaderDataWithProxyModel();
87
88private:
89 void generic_data(const QString &engine=QString());
90 void dropTestTables(QSqlDatabase db);
91 void createTestTables(QSqlDatabase db);
92 void populateTestTables(QSqlDatabase db);
93 tst_Databases dbs;
94};
95
96/* Stupid class that makes protected members public for testing */
97class DBTestModel: public QSqlQueryModel
98{
99public:
100 DBTestModel(QObject *parent = 0): QSqlQueryModel(parent) {}
101 QModelIndex indexInQuery(const QModelIndex &item) const { return QSqlQueryModel::indexInQuery(item); }
102};
103
104tst_QSqlQueryModel::tst_QSqlQueryModel()
105{
106}
107
108tst_QSqlQueryModel::~tst_QSqlQueryModel()
109{
110}
111
112void tst_QSqlQueryModel::initTestCase()
113{
114 QVERIFY(dbs.open());
115 for (QStringList::ConstIterator it = dbs.dbNames.begin(); it != dbs.dbNames.end(); ++it) {
116 QSqlDatabase db = QSqlDatabase::database(connectionName: (*it));
117 CHECK_DATABASE(db);
118 dropTestTables(db); //in case of leftovers
119 createTestTables(db);
120 populateTestTables(db);
121 }
122}
123
124void tst_QSqlQueryModel::cleanupTestCase()
125{
126 for (QStringList::ConstIterator it = dbs.dbNames.begin(); it != dbs.dbNames.end(); ++it) {
127 QSqlDatabase db = QSqlDatabase::database(connectionName: (*it));
128 CHECK_DATABASE(db);
129 dropTestTables(db);
130 }
131 dbs.close();
132}
133
134void tst_QSqlQueryModel::dropTestTables(QSqlDatabase db)
135{
136 QStringList tableNames;
137 tableNames << qTableName(prefix: "test", __FILE__, db)
138 << qTableName(prefix: "test2", __FILE__, db)
139 << qTableName(prefix: "test3", __FILE__, db)
140 << qTableName(prefix: "many", __FILE__, db);
141 tst_Databases::safeDropTables(db, tableNames);
142}
143
144void tst_QSqlQueryModel::createTestTables(QSqlDatabase db)
145{
146 dropTestTables(db);
147 QSqlQuery q(db);
148 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
149 if (dbType == QSqlDriver::PostgreSQL)
150 QVERIFY_SQL( q, exec("set client_min_messages='warning'"));
151 QVERIFY_SQL( q, exec("create table " + qTableName("test", __FILE__, db) + "(id integer not null, name varchar(20), title integer, primary key (id))"));
152 QVERIFY_SQL( q, exec("create table " + qTableName("test2", __FILE__, db) + "(id integer not null, title varchar(20), primary key (id))"));
153 QVERIFY_SQL( q, exec("create table " + qTableName("test3", __FILE__, db) + "(id integer not null, primary key (id))"));
154 QVERIFY_SQL( q, exec("create table " + qTableName("many", __FILE__, db) + "(id integer not null, name varchar(20), primary key (id))"));
155}
156
157void tst_QSqlQueryModel::populateTestTables(QSqlDatabase db)
158{
159 qWarning() << "Populating test tables, this can take quite a while... ZZZzzz...";
160 bool hasTransactions = db.driver()->hasFeature(f: QSqlDriver::Transactions);
161
162 QSqlQuery q(db), q2(db);
163
164 tst_Databases::safeDropTables(db, tableNames: QStringList() << qTableName(prefix: "manytmp", __FILE__, db) << qTableName(prefix: "test3tmp", __FILE__, db));
165 QVERIFY_SQL(q, exec("create table " + qTableName("manytmp", __FILE__, db) + "(id integer not null, name varchar(20), primary key (id))"));
166 QVERIFY_SQL(q, exec("create table " + qTableName("test3tmp", __FILE__, db) + "(id integer not null, primary key (id))"));
167
168 if (hasTransactions) QVERIFY_SQL(db, transaction());
169
170 QVERIFY_SQL(q, exec("insert into " + qTableName("test", __FILE__, db) + " values(1, 'harry', 1)"));
171 QVERIFY_SQL(q, exec("insert into " + qTableName("test", __FILE__, db) + " values(2, 'trond', 2)"));
172 QVERIFY_SQL(q, exec("insert into " + qTableName("test2", __FILE__, db) + " values(1, 'herr')"));
173 QVERIFY_SQL(q, exec("insert into " + qTableName("test2", __FILE__, db) + " values(2, 'mister')"));
174
175 QVERIFY_SQL(q, exec(QString("insert into " + qTableName("test3", __FILE__, db) + " values(0)")));
176 QVERIFY_SQL(q, prepare("insert into "+ qTableName("test3", __FILE__, db) + "(id) select id + ? from " + qTableName("test3tmp", __FILE__, db)));
177 for (int i=1; i<260; i*=2) {
178 q2.exec(query: "delete from " + qTableName(prefix: "test3tmp", __FILE__, db));
179 QVERIFY_SQL(q2, exec("insert into " + qTableName("test3tmp", __FILE__, db) + "(id) select id from " + qTableName("test3", __FILE__, db)));
180 q.bindValue(pos: 0, val: i);
181 QVERIFY_SQL(q, exec());
182 }
183
184 QVERIFY_SQL(q, exec(QString("insert into " + qTableName("many", __FILE__, db) + "(id, name) values (0, \'harry\')")));
185 QVERIFY_SQL(q, prepare("insert into " + qTableName("many", __FILE__, db) + "(id, name) select id + ?, name from " + qTableName("manytmp", __FILE__, db)));
186 for (int i=1; i < 2048; i*=2) {
187 q2.exec(query: "delete from " + qTableName(prefix: "manytmp", __FILE__, db));
188 QVERIFY_SQL(q2, exec("insert into " + qTableName("manytmp", __FILE__, db) + "(id, name) select id, name from " + qTableName("many", __FILE__, db)));
189 q.bindValue(pos: 0, val: i);
190 QVERIFY_SQL(q, exec());
191 }
192
193 if (hasTransactions) QVERIFY_SQL(db, commit());
194
195 tst_Databases::safeDropTables(db, tableNames: QStringList() << qTableName(prefix: "manytmp", __FILE__, db) << qTableName(prefix: "test3tmp", __FILE__, db));
196}
197
198void tst_QSqlQueryModel::generic_data(const QString& engine)
199{
200 if ( dbs.fillTestTable(driverPrefix: engine) == 0 ) {
201 if(engine.isEmpty())
202 QSKIP( "No database drivers are available in this Qt configuration");
203 else
204 QSKIP( (QString("No database drivers of type %1 are available in this Qt configuration").arg(engine)).toLocal8Bit());
205 }
206}
207
208void tst_QSqlQueryModel::init()
209{
210}
211
212void tst_QSqlQueryModel::cleanup()
213{
214}
215
216void tst_QSqlQueryModel::removeColumn()
217{
218 QFETCH(QString, dbName);
219 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
220 CHECK_DATABASE(db);
221
222 DBTestModel model;
223 model.setQuery(QSqlQuery("select * from " + qTableName(prefix: "test", __FILE__, db), db));
224 model.fetchMore();
225 QSignalSpy spy(&model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)));
226
227 QCOMPARE(model.columnCount(), 3);
228 QVERIFY(model.removeColumn(0));
229 QCOMPARE(spy.count(), 1);
230 QVERIFY(*(QModelIndex *)spy.at(0).at(0).constData() == QModelIndex());
231 QCOMPARE(spy.at(0).at(1).toInt(), 0);
232 QCOMPARE(spy.at(0).at(2).toInt(), 0);
233
234 QCOMPARE(model.columnCount(), 2);
235 QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 1);
236 QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), 2);
237 QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1);
238 QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1);
239
240 QVERIFY(model.insertColumn(1));
241 QCOMPARE(model.columnCount(), 3);
242 QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 1);
243 QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1);
244 QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), 2);
245 QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1);
246
247 QCOMPARE(model.data(model.index(0, 0)).toString(), QString("harry"));
248 QCOMPARE(model.data(model.index(0, 1)), QVariant());
249 QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
250 QCOMPARE(model.data(model.index(0, 3)), QVariant());
251
252 QVERIFY(!model.removeColumn(42));
253 QVERIFY(!model.removeColumn(3));
254 QVERIFY(!model.removeColumn(1, model.index(1, 2)));
255 QCOMPARE(model.columnCount(), 3);
256
257 QVERIFY(model.removeColumn(2));
258
259 QCOMPARE(spy.count(), 2);
260 QVERIFY(*(QModelIndex *)spy.at(1).at(0).constData() == QModelIndex());
261 QCOMPARE(spy.at(1).at(1).toInt(), 2);
262 QCOMPARE(spy.at(1).at(2).toInt(), 2);
263
264 QCOMPARE(model.columnCount(), 2);
265 QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 1);
266 QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1);
267 QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1);
268 QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1);
269
270 QVERIFY(model.removeColumn(1));
271
272 QCOMPARE(spy.count(), 3);
273 QVERIFY(*(QModelIndex *)spy.at(2).at(0).constData() == QModelIndex());
274 QCOMPARE(spy.at(2).at(1).toInt(), 1);
275 QCOMPARE(spy.at(2).at(2).toInt(), 1);
276
277 QCOMPARE(model.columnCount(), 1);
278 QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 1);
279 QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1);
280 QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1);
281 QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1);
282 QCOMPARE(model.data(model.index(0, 0)).toString(), QString("harry"));
283
284 QVERIFY(model.removeColumn(0));
285
286 QCOMPARE(spy.count(), 4);
287 QVERIFY(*(QModelIndex *)spy.at(3).at(0).constData() == QModelIndex());
288 QCOMPARE(spy.at(3).at(1).toInt(), 0);
289 QCOMPARE(spy.at(3).at(2).toInt(), 0);
290
291 QCOMPARE(model.columnCount(), 0);
292 QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), -1);
293 QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1);
294 QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1);
295 QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1);
296}
297
298void tst_QSqlQueryModel::insertColumn()
299{
300 QFETCH(QString, dbName);
301 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
302 CHECK_DATABASE(db);
303 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
304
305 DBTestModel model;
306 model.setQuery(QSqlQuery("select * from " + qTableName(prefix: "test", __FILE__, db), db));
307 model.fetchMore(); // necessary???
308
309 bool isToUpper = (dbType == QSqlDriver::Interbase) || (dbType == QSqlDriver::Oracle) || (dbType == QSqlDriver::DB2);
310 const QString idColumn(isToUpper ? "ID" : "id");
311 const QString nameColumn(isToUpper ? "NAME" : "name");
312 const QString titleColumn(isToUpper ? "TITLE" : "title");
313
314 QSignalSpy spy(&model, SIGNAL(columnsInserted(QModelIndex,int,int)));
315
316 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
317 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
318 QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
319 QCOMPARE(model.data(model.index(0, 3)), QVariant());
320
321 QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), idColumn);
322 QCOMPARE(model.headerData(1, Qt::Horizontal).toString(), nameColumn);
323 QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), titleColumn);
324 QCOMPARE(model.headerData(3, Qt::Horizontal).toString(), QString("4"));
325
326 QVERIFY(model.insertColumn(1));
327
328 QCOMPARE(spy.count(), 1);
329 QVERIFY(*(QModelIndex *)spy.at(0).at(0).constData() == QModelIndex());
330 QCOMPARE(spy.at(0).at(1).toInt(), 1);
331 QCOMPARE(spy.at(0).at(2).toInt(), 1);
332
333 QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 0);
334 QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1);
335 QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), 1);
336 QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), 2);
337 QCOMPARE(model.indexInQuery(model.index(0, 4)).column(), -1);
338
339 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
340 QCOMPARE(model.data(model.index(0, 1)), QVariant());
341 QCOMPARE(model.data(model.index(0, 2)).toString(), QString("harry"));
342 QCOMPARE(model.data(model.index(0, 3)).toInt(), 1);
343 QCOMPARE(model.data(model.index(0, 4)), QVariant());
344
345 QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), idColumn);
346 QCOMPARE(model.headerData(1, Qt::Horizontal).toString(), QString("2"));
347 QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), nameColumn);
348 QCOMPARE(model.headerData(3, Qt::Horizontal).toString(), titleColumn);
349 QCOMPARE(model.headerData(4, Qt::Horizontal).toString(), QString("5"));
350
351 QVERIFY(!model.insertColumn(-1));
352 QVERIFY(!model.insertColumn(100));
353 QVERIFY(!model.insertColumn(1, model.index(1, 1)));
354
355 QVERIFY(model.insertColumn(0));
356
357 QCOMPARE(spy.count(), 2);
358 QVERIFY(*(QModelIndex *)spy.at(1).at(0).constData() == QModelIndex());
359 QCOMPARE(spy.at(1).at(1).toInt(), 0);
360 QCOMPARE(spy.at(1).at(2).toInt(), 0);
361
362 QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), -1);
363 QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), 0);
364 QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1);
365 QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), 1);
366 QCOMPARE(model.indexInQuery(model.index(0, 4)).column(), 2);
367 QCOMPARE(model.indexInQuery(model.index(0, 5)).column(), -1);
368
369 QVERIFY(!model.insertColumn(6));
370 QVERIFY(model.insertColumn(5));
371
372 QCOMPARE(spy.count(), 3);
373 QVERIFY(*(QModelIndex *)spy.at(2).at(0).constData() == QModelIndex());
374 QCOMPARE(spy.at(2).at(1).toInt(), 5);
375 QCOMPARE(spy.at(2).at(2).toInt(), 5);
376
377 QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), -1);
378 QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), 0);
379 QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1);
380 QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), 1);
381 QCOMPARE(model.indexInQuery(model.index(0, 4)).column(), 2);
382 QCOMPARE(model.indexInQuery(model.index(0, 5)).column(), -1);
383 QCOMPARE(model.indexInQuery(model.index(0, 6)).column(), -1);
384
385 QCOMPARE(model.record().field(0).name(), QString());
386 QCOMPARE(model.record().field(1).name(), idColumn);
387 QCOMPARE(model.record().field(2).name(), QString());
388 QCOMPARE(model.record().field(3).name(), nameColumn);
389 QCOMPARE(model.record().field(4).name(), titleColumn);
390 QCOMPARE(model.record().field(5).name(), QString());
391 QCOMPARE(model.record().field(6).name(), QString());
392
393 QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("1"));
394 QCOMPARE(model.headerData(1, Qt::Horizontal).toString(), idColumn);
395 QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), QString("3"));
396 QCOMPARE(model.headerData(3, Qt::Horizontal).toString(), nameColumn);
397 QCOMPARE(model.headerData(4, Qt::Horizontal).toString(), titleColumn);
398 QCOMPARE(model.headerData(5, Qt::Horizontal).toString(), QString("6"));
399 QCOMPARE(model.headerData(6, Qt::Horizontal).toString(), QString("7"));
400}
401
402void tst_QSqlQueryModel::record()
403{
404 QFETCH(QString, dbName);
405 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
406 CHECK_DATABASE(db);
407 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
408
409 QSqlQueryModel model;
410 model.setQuery(QSqlQuery("select * from " + qTableName(prefix: "test", __FILE__, db), db));
411
412 QSqlRecord rec = model.record();
413
414 bool isToUpper = (dbType == QSqlDriver::Interbase) || (dbType == QSqlDriver::Oracle) || (dbType == QSqlDriver::DB2);
415
416 QCOMPARE(rec.count(), 3);
417 QCOMPARE(rec.fieldName(0), isToUpper ? QString("ID") : QString("id"));
418 QCOMPARE(rec.fieldName(1), isToUpper ? QString("NAME") : QString("name"));
419 QCOMPARE(rec.fieldName(2), isToUpper ? QString("TITLE") : QString("title"));
420 QCOMPARE(rec.value(0), QVariant(rec.field(0).type()));
421 QCOMPARE(rec.value(1), QVariant(rec.field(1).type()));
422 QCOMPARE(rec.value(2), QVariant(rec.field(2).type()));
423
424 rec = model.record(row: 0);
425 QCOMPARE(rec.fieldName(0), isToUpper ? QString("ID") : QString("id"));
426 QCOMPARE(rec.fieldName(1), isToUpper ? QString("NAME") : QString("name"));
427 QCOMPARE(rec.fieldName(2), isToUpper ? QString("TITLE") : QString("title"));
428 QCOMPARE(rec.value(0).toString(), QString("1"));
429 QCOMPARE(rec.value(1), QVariant("harry"));
430 QCOMPARE(rec.value(2), QVariant(1));
431}
432
433void tst_QSqlQueryModel::setHeaderData()
434{
435 QFETCH(QString, dbName);
436 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
437 CHECK_DATABASE(db);
438 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
439
440 QSqlQueryModel model;
441
442 QVERIFY(!model.setHeaderData(5, Qt::Vertical, "foo"));
443 QVERIFY(model.headerData(5, Qt::Vertical).isValid());
444
445 model.setQuery(QSqlQuery("select * from " + qTableName(prefix: "test", __FILE__, db), db));
446
447 qRegisterMetaType<Qt::Orientation>(typeName: "Qt::Orientation");
448 QSignalSpy spy(&model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)));
449 QVERIFY(model.setHeaderData(2, Qt::Horizontal, "bar"));
450 QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), QString("bar"));
451 QCOMPARE(spy.count(), 1);
452 QCOMPARE(qvariant_cast<Qt::Orientation>(spy.value(0).value(0)), Qt::Horizontal);
453 QCOMPARE(spy.value(0).value(1).toInt(), 2);
454 QCOMPARE(spy.value(0).value(2).toInt(), 2);
455
456 QVERIFY(!model.setHeaderData(7, Qt::Horizontal, "foo", Qt::ToolTipRole));
457 QVERIFY(!model.headerData(7, Qt::Horizontal, Qt::ToolTipRole).isValid());
458
459 bool isToUpper = (dbType == QSqlDriver::Interbase) || (dbType == QSqlDriver::Oracle) || (dbType == QSqlDriver::DB2);
460 QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), isToUpper ? QString("ID") : QString("id"));
461 QCOMPARE(model.headerData(1, Qt::Horizontal).toString(), isToUpper ? QString("NAME") : QString("name"));
462 QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), QString("bar"));
463 QVERIFY(model.headerData(3, Qt::Horizontal).isValid());
464}
465
466void tst_QSqlQueryModel::fetchMore()
467{
468 QFETCH(QString, dbName);
469 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
470 CHECK_DATABASE(db);
471
472 QSqlQueryModel model;
473 QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset()));
474 QSignalSpy modelResetSpy(&model, SIGNAL(modelReset()));
475
476 model.setQuery(QSqlQuery("select * from " + qTableName(prefix: "many", __FILE__, db), db));
477 int rowCount = model.rowCount();
478
479 QCOMPARE(modelAboutToBeResetSpy.count(), 1);
480 QCOMPARE(modelResetSpy.count(), 1);
481
482 // If the driver doesn't return the query size fetchMore() causes the
483 // model to grow and new signals are emitted
484 QSignalSpy rowsInsertedSpy(&model, SIGNAL(rowsInserted(QModelIndex,int,int)));
485 if (!db.driver()->hasFeature(f: QSqlDriver::QuerySize)) {
486 model.fetchMore();
487 int newRowCount = model.rowCount();
488 QCOMPARE(rowsInsertedSpy.value(0).value(1).toInt(), rowCount);
489 QCOMPARE(rowsInsertedSpy.value(0).value(2).toInt(), newRowCount - 1);
490 }
491}
492
493// For task 149491: When used with QSortFilterProxyModel, a view and a
494// database that doesn't support the QuerySize feature, blank rows was
495// appended if the query returned more than 256 rows and setQuery()
496// was called more than once. This because an insertion of rows was
497// triggered at the same time as the model was being cleared.
498void tst_QSqlQueryModel::withSortFilterProxyModel()
499{
500 QFETCH(QString, dbName);
501 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
502 CHECK_DATABASE(db);
503
504 if (db.driver()->hasFeature(f: QSqlDriver::QuerySize))
505 QSKIP("Test applies only for drivers not reporting the query size.");
506
507 QSqlQueryModel model;
508 model.setQuery(QSqlQuery("SELECT * FROM " + qTableName(prefix: "test3", __FILE__, db), db));
509 QSortFilterProxyModel proxy;
510 proxy.setSourceModel(&model);
511
512 QTableView view;
513 view.setModel(&proxy);
514
515 QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset()));
516 QSignalSpy modelResetSpy(&model, SIGNAL(modelReset()));
517 QSignalSpy modelRowsInsertedSpy(&model, SIGNAL(rowsInserted(QModelIndex,int,int)));
518 model.setQuery(QSqlQuery("SELECT * FROM " + qTableName(prefix: "test3", __FILE__, db), db));
519 view.scrollToBottom();
520
521 QTestEventLoop::instance().enterLoop(secs: 1);
522
523 QCOMPARE(proxy.rowCount(), 511);
524
525 // setQuery() resets the model accompanied by begin and end signals
526 QCOMPARE(modelAboutToBeResetSpy.count(), 1);
527 QCOMPARE(modelResetSpy.count(), 1);
528
529 // The call to scrollToBottom() forces the model to fetch additional rows.
530 QCOMPARE(modelRowsInsertedSpy.count(), 1);
531 QCOMPARE(modelRowsInsertedSpy.value(0).value(1).toInt(), 256);
532 QCOMPARE(modelRowsInsertedSpy.value(0).value(2).toInt(), 510);
533}
534
535// For task 155402: When the model is already empty when setQuery() is called
536// no rows have to be removed and rowsAboutToBeRemoved and rowsRemoved should
537// not be emitted.
538void tst_QSqlQueryModel::setQuerySignalEmission()
539{
540 QFETCH(QString, dbName);
541 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
542 CHECK_DATABASE(db);
543
544 QSqlQueryModel model;
545 QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset()));
546 QSignalSpy modelResetSpy(&model, SIGNAL(modelReset()));
547
548 // First select, the model was empty and no rows had to be removed, but model resets anyway.
549 model.setQuery(QSqlQuery("SELECT * FROM " + qTableName(prefix: "test", __FILE__, db), db));
550 QCOMPARE(modelAboutToBeResetSpy.count(), 1);
551 QCOMPARE(modelResetSpy.count(), 1);
552
553 // Second select, the model wasn't empty and two rows had to be removed!
554 // setQuery() resets the model accompanied by begin and end signals
555 model.setQuery(QSqlQuery("SELECT * FROM " + qTableName(prefix: "test", __FILE__, db), db));
556 QCOMPARE(modelAboutToBeResetSpy.count(), 2);
557 QCOMPARE(modelResetSpy.count(), 2);
558}
559
560// For task 170783: When the query's result set is empty no rows should be inserted,
561// i.e. no rowsAboutToBeInserted or rowsInserted signals should be emitted.
562void tst_QSqlQueryModel::setQueryWithNoRowsInResultSet()
563{
564 QFETCH(QString, dbName);
565 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
566 CHECK_DATABASE(db);
567
568 QSqlQueryModel model;
569 QSignalSpy modelRowsAboutToBeInsertedSpy(&model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)));
570 QSignalSpy modelRowsInsertedSpy(&model, SIGNAL(rowsInserted(QModelIndex,int,int)));
571
572 // The query's result set will be empty so no signals should be emitted!
573 QSqlQuery query(db);
574 QVERIFY_SQL(query, exec("SELECT * FROM " + qTableName("test", __FILE__, db) + " where 0 = 1"));
575 model.setQuery(query);
576 QCOMPARE(modelRowsAboutToBeInsertedSpy.count(), 0);
577 QCOMPARE(modelRowsInsertedSpy.count(), 0);
578}
579
580class NestedResetsTest: public QSqlQueryModel
581{
582 Q_OBJECT
583
584public:
585 NestedResetsTest(QObject* parent = 0) : QSqlQueryModel(parent), gotAboutToBeReset(false), gotReset(false)
586 {
587 connect(sender: this, SIGNAL(modelAboutToBeReset()), receiver: this, SLOT(modelAboutToBeResetSlot()));
588 connect(sender: this, SIGNAL(modelReset()), receiver: this, SLOT(modelResetSlot()));
589 }
590
591 void testNested()
592 {
593 // Only the outermost beginResetModel/endResetModel should
594 // emit signals.
595 gotAboutToBeReset = gotReset = false;
596 beginResetModel();
597 QCOMPARE(gotAboutToBeReset, true);
598 QCOMPARE(gotReset, false);
599
600 gotAboutToBeReset = gotReset = false;
601 beginResetModel();
602 QCOMPARE(gotAboutToBeReset, false);
603 QCOMPARE(gotReset, false);
604
605 gotAboutToBeReset = gotReset = false;
606 endResetModel();
607 QCOMPARE(gotAboutToBeReset, false);
608 QCOMPARE(gotReset, false);
609
610 gotAboutToBeReset = gotReset = false;
611 endResetModel();
612 QCOMPARE(gotAboutToBeReset, false);
613 QCOMPARE(gotReset, true);
614 }
615
616 void testClear() // QTBUG-49404: Basic test whether clear() emits signals.
617 {
618 gotAboutToBeReset = gotReset = false;
619 clear();
620 QVERIFY(gotAboutToBeReset);
621 QVERIFY(gotReset);
622 }
623
624private slots:
625 void modelAboutToBeResetSlot() { gotAboutToBeReset = true; }
626 void modelResetSlot() { gotReset = true; }
627
628private:
629 bool gotAboutToBeReset;
630 bool gotReset;
631};
632
633void tst_QSqlQueryModel::nestedResets()
634{
635 QFETCH(QString, dbName);
636 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
637 CHECK_DATABASE(db);
638
639 NestedResetsTest t;
640 t.testClear();
641 t.testNested();
642}
643
644// For task 180617
645// According to the task, several specific duplicate SQL queries would cause
646// multiple empty grid lines to be visible in the view
647void tst_QSqlQueryModel::task_180617()
648{
649 QFETCH(QString, dbName);
650 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
651 CHECK_DATABASE(db);
652 const QString test3(qTableName(prefix: "test3", __FILE__, db));
653
654 QTableView view;
655 QCOMPARE(view.columnAt(0), -1);
656 QCOMPARE(view.rowAt(0), -1);
657
658 QSqlQueryModel model;
659 model.setQuery( query: "SELECT TOP 0 * FROM " + test3, db );
660 view.setModel(&model);
661
662 bool error = false;
663 // Usually a syntax error
664 if (model.lastError().isValid()) // usually a syntax error
665 error = true;
666
667 QCOMPARE(view.columnAt(0), (error)?-1:0 );
668 QCOMPARE(view.rowAt(0), -1);
669
670 model.setQuery( query: "SELECT TOP 0 * FROM " + test3, db );
671 model.setQuery( query: "SELECT TOP 0 * FROM " + test3, db );
672 model.setQuery( query: "SELECT TOP 0 * FROM " + test3, db );
673 model.setQuery( query: "SELECT TOP 0 * FROM " + test3, db );
674
675 QCOMPARE(view.columnAt(0), (error)?-1:0 );
676 QCOMPARE(view.rowAt(0), -1);
677}
678
679void tst_QSqlQueryModel::task_QTBUG_4963_setHeaderDataWithProxyModel()
680{
681 QSqlQueryModel plainModel;
682 QSortFilterProxyModel proxyModel;
683 proxyModel.setSourceModel(&plainModel);
684 QVERIFY(!plainModel.setHeaderData(0, Qt::Horizontal, QObject::tr("ID")));
685 // And it should not crash.
686}
687
688QTEST_MAIN(tst_QSqlQueryModel)
689#include "tst_qsqlquerymodel.moc"
690

source code of qtbase/tests/auto/sql/models/qsqlquerymodel/tst_qsqlquerymodel.cpp