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 | |
46 | Q_DECLARE_METATYPE(Qt::Orientation) |
47 | |
48 | class tst_QSqlQueryModel : public QObject |
49 | { |
50 | Q_OBJECT |
51 | |
52 | public: |
53 | tst_QSqlQueryModel(); |
54 | virtual ~tst_QSqlQueryModel(); |
55 | |
56 | public slots: |
57 | void initTestCase(); |
58 | void cleanupTestCase(); |
59 | void init(); |
60 | void cleanup(); |
61 | |
62 | private 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 () { 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 | |
88 | private: |
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 */ |
97 | class DBTestModel: public QSqlQueryModel |
98 | { |
99 | public: |
100 | DBTestModel(QObject *parent = 0): QSqlQueryModel(parent) {} |
101 | QModelIndex indexInQuery(const QModelIndex &item) const { return QSqlQueryModel::indexInQuery(item); } |
102 | }; |
103 | |
104 | tst_QSqlQueryModel::tst_QSqlQueryModel() |
105 | { |
106 | } |
107 | |
108 | tst_QSqlQueryModel::~tst_QSqlQueryModel() |
109 | { |
110 | } |
111 | |
112 | void 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 | |
124 | void 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 | |
134 | void 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 | |
144 | void 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 | |
157 | void 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 | |
198 | void 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 | |
208 | void tst_QSqlQueryModel::init() |
209 | { |
210 | } |
211 | |
212 | void tst_QSqlQueryModel::cleanup() |
213 | { |
214 | } |
215 | |
216 | void 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 | |
298 | void 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 | |
402 | void 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 | |
433 | void tst_QSqlQueryModel::() |
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 | |
466 | void 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. |
498 | void 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. |
538 | void 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. |
562 | void 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 | |
580 | class NestedResetsTest: public QSqlQueryModel |
581 | { |
582 | Q_OBJECT |
583 | |
584 | public: |
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 | |
624 | private slots: |
625 | void modelAboutToBeResetSlot() { gotAboutToBeReset = true; } |
626 | void modelResetSlot() { gotReset = true; } |
627 | |
628 | private: |
629 | bool gotAboutToBeReset; |
630 | bool gotReset; |
631 | }; |
632 | |
633 | void 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 |
647 | void 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 | |
679 | void tst_QSqlQueryModel::() |
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 | |
688 | QTEST_MAIN(tst_QSqlQueryModel) |
689 | #include "tst_qsqlquerymodel.moc" |
690 | |