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 "../../kernel/qsqldatabase/tst_databases.h"
32#include <QtSql>
33#include <QtSql/private/qsqltablemodel_p.h>
34#include <QThread>
35#include <QElapsedTimer>
36
37const QString test(qTableName(prefix: "test", __FILE__, db: QSqlDatabase())),
38 test2(qTableName(prefix: "test2", __FILE__, db: QSqlDatabase())),
39 test3(qTableName(prefix: "test3", __FILE__, db: QSqlDatabase()));
40
41// In order to catch when the warning message occurs, indicating that the database belongs to another
42// thread, we have to install our own message handler. To ensure that the test reporting still happens
43// as before, we call the originating one.
44//
45// For now, this is only called inside the modelInAnotherThread() test
46QtMessageHandler oldHandler = nullptr;
47
48void sqlTableModelMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
49{
50 if (type == QtWarningMsg &&
51 msg == "QSqlDatabasePrivate::database: requested database does not "
52 "belong to the calling thread.") {
53 QFAIL("Requested database does not belong to the calling thread.");
54 }
55 if (oldHandler)
56 oldHandler(type, context, msg);
57}
58
59class tst_QSqlTableModel : public QObject
60{
61 Q_OBJECT
62
63public:
64 tst_QSqlTableModel();
65 virtual ~tst_QSqlTableModel();
66
67
68 void dropTestTables();
69 void createTestTables();
70 void recreateTestTables();
71 void repopulateTestTables();
72
73 tst_Databases dbs;
74
75public slots:
76 void initTestCase();
77 void cleanupTestCase();
78 void init();
79 void cleanup();
80private slots:
81
82 void select_data() { generic_data(); }
83 void select();
84 void selectRow_data() { generic_data(); }
85 void selectRow();
86 void selectRowOverride_data() { generic_data(); }
87 void selectRowOverride();
88 void insertColumns_data() { generic_data_with_strategies(); }
89 void insertColumns();
90 void submitAll_data() { generic_data(); }
91 void submitAll();
92 void setData_data() { generic_data(); }
93 void setData();
94 void setRecord_data() { generic_data(); }
95 void setRecord();
96 void setRecordReimpl_data() { generic_data(); }
97 void setRecordReimpl();
98 void recordReimpl_data() { generic_data(); }
99 void recordReimpl();
100 void insertRow_data() { generic_data_with_strategies(); }
101 void insertRow();
102 void insertRowFailure_data() { generic_data_with_strategies(); }
103 void insertRowFailure();
104 void insertRecord_data() { generic_data(); }
105 void insertRecord();
106 void insertMultiRecords_data() { generic_data(); }
107 void insertMultiRecords();
108 void insertWithAutoColumn_data() { generic_data_with_strategies(engine: "QSQLITE"); }
109 void insertWithAutoColumn();
110 void removeRow_data() { generic_data(); }
111 void removeRow();
112 void removeRows_data() { generic_data(); }
113 void removeRows();
114 void removeInsertedRow_data() { generic_data_with_strategies(); }
115 void removeInsertedRow();
116 void removeInsertedRows_data() { generic_data(); }
117 void removeInsertedRows();
118 void revert_data() { generic_data_with_strategies(engine: "QSQLITE"); }
119 void revert();
120 void isDirty_data() { generic_data_with_strategies(); }
121 void isDirty();
122 void setFilter_data() { generic_data(); }
123 void setFilter();
124 void setInvalidFilter_data() { generic_data(); }
125 void setInvalidFilter();
126
127 void emptyTable_data() { generic_data(); }
128 void emptyTable();
129 void tablesAndSchemas_data() { generic_data(engine: "QPSQL"); }
130 void tablesAndSchemas();
131 void whitespaceInIdentifiers_data() { generic_data(); }
132 void whitespaceInIdentifiers();
133 void primaryKeyOrder_data() { generic_data(engine: "QSQLITE"); }
134 void primaryKeyOrder();
135
136 void sqlite_bigTable_data() { generic_data(engine: "QSQLITE"); }
137 void sqlite_bigTable();
138 void modelInAnotherThread();
139
140 // bug specific tests
141 void insertRecordBeforeSelect_data() { generic_data(); }
142 void insertRecordBeforeSelect();
143 void submitAllOnInvalidTable_data() { generic_data(); }
144 void submitAllOnInvalidTable();
145 void insertRecordsInLoop_data() { generic_data(); }
146 void insertRecordsInLoop();
147 void sqlite_attachedDatabase_data() { generic_data(engine: "QSQLITE"); }
148 void sqlite_attachedDatabase(); // For task 130799
149 void tableModifyWithBlank_data() { generic_data(); }
150 void tableModifyWithBlank(); // For mail task
151
152 void removeColumnAndRow_data() { generic_data(); }
153 void removeColumnAndRow(); // task 256032
154
155 void insertBeforeDelete_data() { generic_data(); }
156 void insertBeforeDelete();
157
158 void invalidFilterAndHeaderData_data() { generic_data(); }
159 void invalidFilterAndHeaderData(); //QTBUG-23879
160
161 void sqlite_selectFromIdentifierWithDot_data() { generic_data(engine: "QSQLITE"); }
162 void sqlite_selectFromIdentifierWithDot();
163private:
164 void generic_data(const QString& engine=QString());
165 void generic_data_with_strategies(const QString& engine=QString());
166};
167
168tst_QSqlTableModel::tst_QSqlTableModel()
169{
170 QVERIFY(dbs.open());
171}
172
173tst_QSqlTableModel::~tst_QSqlTableModel()
174{
175}
176
177void tst_QSqlTableModel::dropTestTables()
178{
179 for (int i = 0; i < dbs.dbNames.count(); ++i) {
180 QSqlDatabase db = QSqlDatabase::database(connectionName: dbs.dbNames.at(i));
181 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
182 QSqlQuery q(db);
183 if (dbType == QSqlDriver::PostgreSQL)
184 QVERIFY_SQL( q, exec("set client_min_messages='warning'"));
185
186 QStringList tableNames;
187 tableNames << test
188 << test2
189 << test3
190 << qTableName(prefix: "test4", __FILE__, db)
191 << qTableName(prefix: "emptytable", __FILE__, db)
192 << qTableName(prefix: "bigtable", __FILE__, db)
193 << qTableName(prefix: "foo", __FILE__, db)
194 << qTableName(prefix: "pktest", __FILE__, db);
195 if (testWhiteSpaceNames(name: db.driverName()))
196 tableNames << qTableName(prefix: "qtestw hitespace", db);
197
198 tst_Databases::safeDropTables(db, tableNames);
199
200 if (db.driverName().startsWith(s: "QPSQL")) {
201 q.exec(query: "DROP SCHEMA " + qTableName(prefix: "testschema", __FILE__, db) + " CASCADE");
202 }
203 }
204}
205
206void tst_QSqlTableModel::createTestTables()
207{
208 for (int i = 0; i < dbs.dbNames.count(); ++i) {
209 QSqlDatabase db = QSqlDatabase::database(connectionName: dbs.dbNames.at(i));
210 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
211 QSqlQuery q(db);
212
213 QVERIFY_SQL( q, exec("create table " + test + "(id int, name varchar(20), title int)"));
214
215 QVERIFY_SQL( q, exec("create table " + test2 + "(id int, title varchar(20))"));
216
217 QVERIFY_SQL( q, exec("create table " + test3 + "(id int, random varchar(20), randomtwo varchar(20))"));
218
219 if (dbType != QSqlDriver::MSSqlServer)
220 QVERIFY_SQL(q, exec("create table " + qTableName("test4", __FILE__, db) + "(column1 varchar(50), column2 varchar(50), column3 varchar(50))"));
221 else
222 QVERIFY_SQL(q, exec("create table " + qTableName("test4", __FILE__, db) + "(column1 varchar(50), column2 varchar(50) NULL, column3 varchar(50))"));
223
224
225 QVERIFY_SQL(q, exec("create table " + qTableName("emptytable", __FILE__, db) + "(id int)"));
226
227 if (testWhiteSpaceNames(name: db.driverName())) {
228 QString qry = "create table " + qTableName(prefix: "qtestw hitespace", db) + " ("+ db.driver()->escapeIdentifier(identifier: "a field", type: QSqlDriver::FieldName) + " int)";
229 QVERIFY_SQL( q, exec(qry));
230 }
231
232 QVERIFY_SQL(q, exec("create table " + qTableName("pktest", __FILE__, db) + "(id int not null primary key, a varchar(20))"));
233 }
234}
235
236void tst_QSqlTableModel::repopulateTestTables()
237{
238 for (int i = 0; i < dbs.dbNames.count(); ++i) {
239 QSqlDatabase db = QSqlDatabase::database(connectionName: dbs.dbNames.at(i));
240 QSqlQuery q(db);
241
242 q.exec(query: "delete from " + test);
243 QVERIFY_SQL( q, exec("insert into " + test + " values(1, 'harry', 1)"));
244 QVERIFY_SQL( q, exec("insert into " + test + " values(2, 'trond', 2)"));
245 QVERIFY_SQL( q, exec("insert into " + test + " values(3, 'vohi', 3)"));
246
247 q.exec(query: "delete from " + test2);
248 QVERIFY_SQL( q, exec("insert into " + test2 + " values(1, 'herr')"));
249 QVERIFY_SQL( q, exec("insert into " + test2 + " values(2, 'mister')"));
250
251 q.exec(query: "delete from " + test3);
252 QVERIFY_SQL( q, exec("insert into " + test3 + " values(1, 'foo', 'bar')"));
253 QVERIFY_SQL( q, exec("insert into " + test3 + " values(2, 'baz', 'joe')"));
254 }
255}
256
257void tst_QSqlTableModel::recreateTestTables()
258{
259 dropTestTables();
260 createTestTables();
261 repopulateTestTables();
262}
263
264void tst_QSqlTableModel::generic_data(const QString &engine)
265{
266 if ( dbs.fillTestTable(driverPrefix: engine) == 0 ) {
267 if (engine.isEmpty())
268 QSKIP( "No database drivers are available in this Qt configuration");
269 else
270 QSKIP( (QString("No database drivers of type %1 are available in this Qt configuration").arg(engine)).toLocal8Bit());
271 }
272}
273
274void tst_QSqlTableModel::generic_data_with_strategies(const QString &engine)
275{
276 if ( dbs.fillTestTableWithStrategies(driverPrefix: engine) == 0 ) {
277 if (engine.isEmpty())
278 QSKIP( "No database drivers are available in this Qt configuration");
279 else
280 QSKIP( (QString("No database drivers of type %1 are available in this Qt configuration").arg(engine)).toLocal8Bit());
281 }
282}
283
284void tst_QSqlTableModel::initTestCase()
285{
286 recreateTestTables();
287}
288
289void tst_QSqlTableModel::cleanupTestCase()
290{
291 dropTestTables();
292 dbs.close();
293}
294
295void tst_QSqlTableModel::init()
296{
297}
298
299void tst_QSqlTableModel::cleanup()
300{
301 recreateTestTables();
302 if (oldHandler) {
303 qInstallMessageHandler(oldHandler);
304 oldHandler = nullptr;
305 }
306}
307
308void tst_QSqlTableModel::select()
309{
310 QFETCH(QString, dbName);
311 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
312 CHECK_DATABASE(db);
313
314 QString withoutQuotes = test;
315 const QStringList tables = {test, withoutQuotes.remove(c: QLatin1Char('"'))};
316 for (const QString &tbl : tables) {
317 QSqlTableModel model(0, db);
318 model.setTable(tbl);
319 model.setSort(column: 0, order: Qt::AscendingOrder);
320 QVERIFY_SQL(model, select());
321
322 QCOMPARE(model.rowCount(), 3);
323 QCOMPARE(model.columnCount(), 3);
324
325 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
326 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
327 QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
328 QCOMPARE(model.data(model.index(0, 3)), QVariant());
329
330 QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
331 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
332 QCOMPARE(model.data(model.index(1, 2)).toInt(), 2);
333 QCOMPARE(model.data(model.index(1, 3)), QVariant());
334
335 QCOMPARE(model.data(model.index(2, 0)).toInt(), 3);
336 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
337 QCOMPARE(model.data(model.index(2, 2)).toInt(), 3);
338 QCOMPARE(model.data(model.index(2, 3)), QVariant());
339
340 QCOMPARE(model.data(model.index(3, 0)), QVariant());
341 QCOMPARE(model.data(model.index(3, 1)), QVariant());
342 QCOMPARE(model.data(model.index(3, 2)), QVariant());
343 QCOMPARE(model.data(model.index(3, 3)), QVariant());
344 }
345}
346
347class SelectRowModel: public QSqlTableModel
348{
349 Q_OBJECT
350 Q_DECLARE_PRIVATE(QSqlTableModel)
351public:
352 SelectRowModel(QObject *parent, QSqlDatabase db): QSqlTableModel(parent, db) {}
353 bool cacheEmpty() const
354 {
355 Q_D(const QSqlTableModel);
356 return d->cache.isEmpty();
357 }
358};
359
360void tst_QSqlTableModel::selectRow()
361{
362 QFETCH(QString, dbName);
363 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
364 CHECK_DATABASE(db);
365
366 QString tbl = qTableName(prefix: "pktest", __FILE__, db);
367 QSqlQuery q(db);
368 q.exec(query: "DELETE FROM " + tbl);
369 q.exec(query: "INSERT INTO " + tbl + " (id, a) VALUES (0, 'a')");
370 q.exec(query: "INSERT INTO " + tbl + " (id, a) VALUES (1, 'b')");
371 q.exec(query: "INSERT INTO " + tbl + " (id, a) VALUES (2, 'c')");
372
373 SelectRowModel model(0, db);
374 model.setEditStrategy(QSqlTableModel::OnFieldChange);
375 model.setTable(tbl);
376 model.setSort(column: 0, order: Qt::AscendingOrder);
377 QVERIFY_SQL(model, select());
378
379 QCOMPARE(model.rowCount(), 3);
380 QCOMPARE(model.columnCount(), 2);
381
382 QModelIndex idx = model.index(row: 1, column: 1);
383
384 // selectRow should not make the cache grow if there is no change.
385 model.selectRow(row: 1);
386 QCOMPARE(model.data(idx).toString(), QString("b"));
387 QVERIFY_SQL(model, cacheEmpty());
388
389 // Check if selectRow() refreshes an unchanged row.
390 // Row is not in cache yet.
391 q.exec(query: "UPDATE " + tbl + " SET a = 'Qt' WHERE id = 1");
392 QCOMPARE(model.data(idx).toString(), QString("b"));
393 model.selectRow(row: 1);
394 QCOMPARE(model.data(idx).toString(), QString("Qt"));
395
396 // Check if selectRow() refreshes a changed row.
397 // Row is already in the cache.
398 model.setData(index: idx, value: QString("b"));
399 QCOMPARE(model.data(idx).toString(), QString("b"));
400 q.exec(query: "UPDATE " + tbl + " SET a = 'Qt' WHERE id = 1");
401 QCOMPARE(model.data(idx).toString(), QString("b"));
402 model.selectRow(row: 1);
403 QCOMPARE(model.data(idx).toString(), QString("Qt"));
404
405 q.exec(query: "DELETE FROM " + tbl);
406}
407
408class SelectRowOverrideTestModel: public QSqlTableModel
409{
410 Q_OBJECT
411public:
412 SelectRowOverrideTestModel(QObject *parent, QSqlDatabase db):QSqlTableModel(parent, db) { }
413 bool selectRow(int row)
414 {
415 Q_UNUSED(row)
416 return select();
417 }
418};
419
420void tst_QSqlTableModel::selectRowOverride()
421{
422 QFETCH(QString, dbName);
423 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
424 CHECK_DATABASE(db);
425
426 QString tbl = qTableName(prefix: "pktest", __FILE__, db);
427 QSqlQuery q(db);
428 q.exec(query: "DELETE FROM " + tbl);
429 q.exec(query: "INSERT INTO " + tbl + " (id, a) VALUES (0, 'a')");
430 q.exec(query: "INSERT INTO " + tbl + " (id, a) VALUES (1, 'b')");
431 q.exec(query: "INSERT INTO " + tbl + " (id, a) VALUES (2, 'c')");
432
433 SelectRowOverrideTestModel model(0, db);
434 model.setEditStrategy(QSqlTableModel::OnFieldChange);
435 model.setTable(tbl);
436 model.setSort(column: 0, order: Qt::AscendingOrder);
437 QVERIFY_SQL(model, select());
438
439 QCOMPARE(model.rowCount(), 3);
440 QCOMPARE(model.columnCount(), 2);
441
442 q.exec(query: "UPDATE " + tbl + " SET a = 'Qt' WHERE id = 2");
443 QModelIndex idx = model.index(row: 1, column: 1);
444 // overridden selectRow() should select() whole table and not crash
445 model.setData(index: idx, value: QString("Qt"));
446
447 // both rows should have changed
448 QCOMPARE(model.data(idx).toString(), QString("Qt"));
449 idx = model.index(row: 2, column: 1);
450 QCOMPARE(model.data(idx).toString(), QString("Qt"));
451
452 q.exec(query: "DELETE FROM " + tbl);
453}
454
455void tst_QSqlTableModel::insertColumns()
456{
457 // Just like the select test, with extra stuff
458 QFETCH(QString, dbName);
459 QFETCH(int, submitpolicy_i);
460 QSqlTableModel::EditStrategy submitpolicy = (QSqlTableModel::EditStrategy) submitpolicy_i;
461 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
462 CHECK_DATABASE(db);
463
464 QSqlTableModel model(0, db);
465 model.setTable(test);
466 model.setSort(column: 0, order: Qt::AscendingOrder);
467 model.setEditStrategy(submitpolicy);
468
469 QVERIFY_SQL(model, select());
470
471 QCOMPARE(model.rowCount(), 3);
472 QCOMPARE(model.columnCount(), 3);
473
474 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
475 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
476 QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
477 QCOMPARE(model.data(model.index(0, 3)), QVariant());
478
479 QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
480 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
481 QCOMPARE(model.data(model.index(1, 2)).toInt(), 2);
482 QCOMPARE(model.data(model.index(1, 3)), QVariant());
483
484 QCOMPARE(model.data(model.index(2, 0)).toInt(), 3);
485 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
486 QCOMPARE(model.data(model.index(2, 2)).toInt(), 3);
487 QCOMPARE(model.data(model.index(2, 3)), QVariant());
488
489 QCOMPARE(model.data(model.index(3, 0)), QVariant());
490 QCOMPARE(model.data(model.index(3, 1)), QVariant());
491 QCOMPARE(model.data(model.index(3, 2)), QVariant());
492 QCOMPARE(model.data(model.index(3, 3)), QVariant());
493
494 // Now add a column at 0 and 2
495 model.insertColumn(acolumn: 0);
496 model.insertColumn(acolumn: 2);
497
498 QCOMPARE(model.rowCount(), 3);
499 QCOMPARE(model.columnCount(), 5);
500
501 QCOMPARE(model.data(model.index(0, 0)), QVariant());
502 QCOMPARE(model.data(model.index(0, 1)).toInt(), 1);
503 QCOMPARE(model.data(model.index(0, 2)), QVariant());
504 QCOMPARE(model.data(model.index(0, 3)).toString(), QString("harry"));
505 QCOMPARE(model.data(model.index(0, 4)).toInt(), 1);
506 QCOMPARE(model.data(model.index(0, 5)), QVariant());
507
508 QCOMPARE(model.data(model.index(1, 0)), QVariant());
509 QCOMPARE(model.data(model.index(1, 1)).toInt(), 2);
510 QCOMPARE(model.data(model.index(1, 2)), QVariant());
511 QCOMPARE(model.data(model.index(1, 3)).toString(), QString("trond"));
512 QCOMPARE(model.data(model.index(1, 4)).toInt(), 2);
513 QCOMPARE(model.data(model.index(1, 5)), QVariant());
514
515 QCOMPARE(model.data(model.index(2, 0)), QVariant());
516 QCOMPARE(model.data(model.index(2, 1)).toInt(), 3);
517 QCOMPARE(model.data(model.index(2, 2)), QVariant());
518 QCOMPARE(model.data(model.index(2, 3)).toString(), QString("vohi"));
519 QCOMPARE(model.data(model.index(2, 4)).toInt(), 3);
520 QCOMPARE(model.data(model.index(2, 5)), QVariant());
521
522 QCOMPARE(model.data(model.index(3, 0)), QVariant());
523 QCOMPARE(model.data(model.index(3, 1)), QVariant());
524 QCOMPARE(model.data(model.index(3, 2)), QVariant());
525 QCOMPARE(model.data(model.index(3, 3)), QVariant());
526 QCOMPARE(model.data(model.index(3, 4)), QVariant());
527 QCOMPARE(model.data(model.index(3, 5)), QVariant());
528}
529
530void tst_QSqlTableModel::setData()
531{
532 QFETCH(QString, dbName);
533 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
534 CHECK_DATABASE(db);
535
536 QSqlTableModel model(0, db);
537 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
538 model.setTable(test);
539 model.setSort(column: 0, order: Qt::AscendingOrder);
540 QVERIFY_SQL(model, select());
541
542 // initial state
543 QModelIndex idx = model.index(row: 0, column: 0);
544 QVariant val = model.data(idx);
545 QVERIFY(val == int(1));
546 QVERIFY(!val.isNull());
547 QFAIL_SQL(model, isDirty());
548
549 // change 1 to 0
550 idx = model.index(row: 0, column: 0);
551 QVERIFY_SQL(model, setData(idx, int(0)));
552 val = model.data(idx);
553 QVERIFY(val == int(0));
554 QVERIFY(!val.isNull());
555 QVERIFY_SQL(model, isDirty(idx));
556 QVERIFY_SQL(model, submitAll());
557
558 // change 0 to NULL
559 idx = model.index(row: 0, column: 0);
560 QVERIFY_SQL(model, setData(idx, QVariant(QVariant::Int)));
561 val = model.data(idx);
562 QCOMPARE(val, QVariant(QVariant::Int));
563 QVERIFY(val.isNull());
564 QVERIFY_SQL(model, isDirty(idx));
565 QVERIFY_SQL(model, submitAll());
566
567 // change NULL to 0
568 idx = model.index(row: 0, column: 0);
569 QVERIFY_SQL(model, setData(idx, int(0)));
570 val = model.data(idx);
571 QVERIFY(val == int(0));
572 QVERIFY(!val.isNull());
573 QVERIFY_SQL(model, isDirty(idx));
574 QVERIFY_SQL(model, submitAll());
575
576 // ignore unchanged 0 to 0
577 idx = model.index(row: 0, column: 0);
578 QVERIFY_SQL(model, setData(idx, int(0)));
579 val = model.data(idx);
580 QVERIFY(val == int(0));
581 QVERIFY(!val.isNull());
582 QFAIL_SQL(model, isDirty(idx));
583
584 // pending INSERT
585 QVERIFY_SQL(model, insertRow(0));
586 // initial state
587 idx = model.index(row: 0, column: 0);
588 QSqlRecord rec = model.record(row: 0);
589 QCOMPARE(rec.value(0), QVariant(QVariant::Int));
590 QVERIFY(rec.isNull(0));
591 QVERIFY(!rec.isGenerated(0));
592 // unchanged value, but causes column to be included in INSERT
593 QVERIFY_SQL(model, setData(idx, QVariant(QVariant::Int)));
594 rec = model.record(row: 0);
595 QCOMPARE(rec.value(0), QVariant(QVariant::Int));
596 QVERIFY(rec.isNull(0));
597 QVERIFY(rec.isGenerated(0));
598 QVERIFY_SQL(model, submitAll());
599}
600
601void tst_QSqlTableModel::setRecord()
602{
603 QFETCH(QString, dbName);
604 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
605 CHECK_DATABASE(db);
606
607 QList<QSqlTableModel::EditStrategy> policies = QList<QSqlTableModel::EditStrategy>() << QSqlTableModel::OnFieldChange << QSqlTableModel::OnRowChange << QSqlTableModel::OnManualSubmit;
608
609 QString Xsuffix;
610 foreach( QSqlTableModel::EditStrategy submitpolicy, policies) {
611
612 QSqlTableModel model(0, db);
613 model.setEditStrategy((QSqlTableModel::EditStrategy)submitpolicy);
614 model.setTable(test3);
615 model.setSort(column: 0, order: Qt::AscendingOrder);
616 QVERIFY_SQL(model, select());
617
618 for (int i = 0; i < model.rowCount(); ++i) {
619 QSignalSpy spy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex)));
620
621 QSqlRecord rec = model.record(row: i);
622 rec.setValue(i: 1, val: rec.value(i: 1).toString() + 'X');
623 rec.setValue(i: 2, val: rec.value(i: 2).toString() + 'X');
624 QVERIFY(model.setRecord(i, rec));
625
626 // dataChanged() emitted by setData() for each *changed* column
627 if ((QSqlTableModel::EditStrategy)submitpolicy == QSqlTableModel::OnManualSubmit) {
628 QCOMPARE(spy.count(), 2);
629 QCOMPARE(spy.at(0).count(), 2);
630 QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(0)), model.index(i, 1));
631 QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(1)), model.index(i, 1));
632 QCOMPARE(qvariant_cast<QModelIndex>(spy.at(1).at(0)), model.index(i, 2));
633 QCOMPARE(qvariant_cast<QModelIndex>(spy.at(1).at(1)), model.index(i, 2));
634 QVERIFY(model.submitAll());
635 } else if ((QSqlTableModel::EditStrategy)submitpolicy == QSqlTableModel::OnRowChange && i == model.rowCount() -1)
636 model.submit();
637 else {
638 if ((QSqlTableModel::EditStrategy)submitpolicy != QSqlTableModel::OnManualSubmit)
639 // dataChanged() also emitted by selectRow()
640 QCOMPARE(spy.count(), 3);
641 else
642 QCOMPARE(spy.count(), 2);
643 QCOMPARE(spy.at(0).count(), 2);
644 QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(0)), model.index(i, 1));
645 QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(1)), model.index(i, 1));
646 QCOMPARE(qvariant_cast<QModelIndex>(spy.at(1).at(0)), model.index(i, 2));
647 QCOMPARE(qvariant_cast<QModelIndex>(spy.at(1).at(1)), model.index(i, 2));
648 }
649 }
650
651 Xsuffix.append(c: 'X');
652
653 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("foo").append(Xsuffix));
654 QCOMPARE(model.data(model.index(0, 2)).toString(), QString("bar").append(Xsuffix));
655 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("baz").append(Xsuffix));
656 QCOMPARE(model.data(model.index(1, 2)).toString(), QString("joe").append(Xsuffix));
657 }
658}
659
660class SetRecordReimplModel: public QSqlTableModel
661{
662 Q_OBJECT
663public:
664 SetRecordReimplModel(QObject *parent, QSqlDatabase db):QSqlTableModel(parent, db) {}
665 bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)
666 {
667 Q_UNUSED(value);
668 return QSqlTableModel::setData(index, value: QString("Qt"), role);
669 }
670};
671
672void tst_QSqlTableModel::setRecordReimpl()
673{
674 QFETCH(QString, dbName);
675 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
676 CHECK_DATABASE(db);
677 SetRecordReimplModel model(0, db);
678 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
679 model.setTable(test3);
680 model.setSort(column: 0, order: Qt::AscendingOrder);
681 QVERIFY_SQL(model, select());
682
683 // make sure that a reimplemented setData() affects setRecord()
684 QSqlRecord rec = model.record(row: 0);
685 rec.setValue(i: 1, val: QString("x"));
686 rec.setValue(i: 2, val: QString("y"));
687 QVERIFY(model.setRecord(0, rec));
688
689 rec = model.record(row: 0);
690 QCOMPARE(rec.value(1).toString(), QString("Qt"));
691 QCOMPARE(rec.value(2).toString(), QString("Qt"));
692}
693
694class RecordReimplModel: public QSqlTableModel
695{
696 Q_OBJECT
697public:
698 RecordReimplModel(QObject *parent, QSqlDatabase db):QSqlTableModel(parent, db) {}
699 QVariant data(const QModelIndex &index, int role = Qt::EditRole) const
700 {
701 if (role == Qt::EditRole)
702 return QString("Qt");
703 else
704 return QSqlTableModel::data(idx: index, role);
705 }
706};
707
708void tst_QSqlTableModel::recordReimpl()
709{
710 QFETCH(QString, dbName);
711 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
712 CHECK_DATABASE(db);
713 RecordReimplModel model(0, db);
714 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
715 model.setTable(test3);
716 model.setSort(column: 0, order: Qt::AscendingOrder);
717 QVERIFY_SQL(model, select());
718
719 // make sure reimplemented data() affects record(row)
720 QSqlRecord rec = model.record(row: 0);
721 QCOMPARE(rec.value(1).toString(), QString("Qt"));
722 QCOMPARE(rec.value(2).toString(), QString("Qt"));
723
724 // and also when the record is in the cache
725 QVERIFY_SQL(model, setData(model.index(0, 1), QString("not Qt")));
726 QVERIFY_SQL(model, setData(model.index(0, 2), QString("not Qt")));
727
728 rec = model.record(row: 0);
729 QCOMPARE(rec.value(1).toString(), QString("Qt"));
730 QCOMPARE(rec.value(2).toString(), QString("Qt"));
731}
732
733void tst_QSqlTableModel::insertRow()
734{
735 QFETCH(QString, dbName);
736 QFETCH(int, submitpolicy_i);
737 QSqlTableModel::EditStrategy submitpolicy = (QSqlTableModel::EditStrategy) submitpolicy_i;
738 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
739 CHECK_DATABASE(db);
740
741 QSqlTableModel model(0, db);
742 model.setEditStrategy(submitpolicy);
743 model.setTable(test);
744 model.setSort(column: 0, order: Qt::AscendingOrder);
745 QVERIFY_SQL(model, select());
746
747 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
748 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
749 QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
750 QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
751 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
752 QCOMPARE(model.data(model.index(1, 2)).toInt(), 2);
753 QCOMPARE(model.data(model.index(2, 0)).toInt(), 3);
754 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
755 QCOMPARE(model.data(model.index(2, 2)).toInt(), 3);
756
757 QVERIFY(model.insertRow(2));
758
759 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
760 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
761 QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
762 QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
763 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
764 QCOMPARE(model.data(model.index(1, 2)).toInt(), 2);
765 QCOMPARE(model.data(model.index(2, 0)).toInt(), 0);
766 QCOMPARE(model.data(model.index(2, 1)).toString(), QString());
767 QCOMPARE(model.data(model.index(2, 2)).toInt(), 0);
768 QCOMPARE(model.data(model.index(3, 0)).toInt(), 3);
769 QCOMPARE(model.data(model.index(3, 1)).toString(), QString("vohi"));
770 QCOMPARE(model.data(model.index(3, 2)).toInt(), 3);
771
772 QSqlRecord rec = model.record(row: 1);
773 rec.setValue(i: 0, val: 42);
774 rec.setValue(i: 1, val: QString("francis"));
775
776 // Setting record does not cause resort
777 QVERIFY(model.setRecord(2, rec));
778
779 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
780 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
781 QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
782 QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
783 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
784 QCOMPARE(model.data(model.index(1, 2)).toInt(), 2);
785
786 QCOMPARE(model.data(model.index(2, 0)).toInt(), 42);
787 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("francis"));
788 QCOMPARE(model.data(model.index(2, 2)).toInt(), 2);
789 QCOMPARE(model.data(model.index(3, 0)).toInt(), 3);
790 QCOMPARE(model.data(model.index(3, 1)).toString(), QString("vohi"));
791 QCOMPARE(model.data(model.index(3, 2)).toInt(), 3);
792
793 QVERIFY(model.submitAll());
794
795 if (submitpolicy == QSqlTableModel::OnManualSubmit) {
796 // After the submit we should have the resorted view
797 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
798 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
799 QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
800 QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
801 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
802 QCOMPARE(model.data(model.index(1, 2)).toInt(), 2);
803 QCOMPARE(model.data(model.index(2, 0)).toInt(), 3);
804 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
805 QCOMPARE(model.data(model.index(2, 2)).toInt(), 3);
806 QCOMPARE(model.data(model.index(3, 0)).toInt(), 42);
807 QCOMPARE(model.data(model.index(3, 1)).toString(), QString("francis"));
808 QCOMPARE(model.data(model.index(3, 2)).toInt(), 2);
809 } else {
810 // Submit does not select, therefore not resorted
811 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
812 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
813 QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
814 QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
815 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
816 QCOMPARE(model.data(model.index(1, 2)).toInt(), 2);
817 QCOMPARE(model.data(model.index(2, 0)).toInt(), 42);
818 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("francis"));
819 QCOMPARE(model.data(model.index(2, 2)).toInt(), 2);
820 QCOMPARE(model.data(model.index(3, 0)).toInt(), 3);
821 QCOMPARE(model.data(model.index(3, 1)).toString(), QString("vohi"));
822 QCOMPARE(model.data(model.index(3, 2)).toInt(), 3);
823 }
824
825 QVERIFY(model.select());
826 // After the select we should have the resorted view in all strategies
827 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
828 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
829 QCOMPARE(model.data(model.index(0, 2)).toInt(), 1);
830 QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
831 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
832 QCOMPARE(model.data(model.index(1, 2)).toInt(), 2);
833 QCOMPARE(model.data(model.index(2, 0)).toInt(), 3);
834 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
835 QCOMPARE(model.data(model.index(2, 2)).toInt(), 3);
836 QCOMPARE(model.data(model.index(3, 0)).toInt(), 42);
837 QCOMPARE(model.data(model.index(3, 1)).toString(), QString("francis"));
838 QCOMPARE(model.data(model.index(3, 2)).toInt(), 2);
839}
840
841void tst_QSqlTableModel::insertRowFailure()
842{
843 QFETCH(QString, dbName);
844 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
845 QFETCH(int, submitpolicy_i);
846 QSqlTableModel::EditStrategy submitpolicy = (QSqlTableModel::EditStrategy) submitpolicy_i;
847 CHECK_DATABASE(db);
848
849 QSqlTableModel model(0, db);
850 model.setTable(qTableName(prefix: "pktest", __FILE__, db));
851 model.setEditStrategy(submitpolicy);
852
853 QSqlRecord values = model.record();
854 values.setValue(i: 0, val: 42);
855 values.setGenerated(i: 0, generated: true);
856 values.setValue(i: 1, val: QString("blah"));
857 values.setGenerated(i: 1, generated: true);
858
859 // populate 1 row
860 const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
861 QVERIFY_SQL(model, insertRecord(0, values));
862 QVERIFY_SQL(model, submitAll());
863 QVERIFY_SQL(model, select());
864 QCOMPARE(model.rowCount(), 1);
865 QCOMPARE(model.data(model.index(0, 0)).toInt(), 42);
866 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("blah"));
867
868 // primary key conflict will succeed in model but fail in database
869 QVERIFY_SQL(model, insertRow(0));
870 QVERIFY_SQL(model, setData(model.index(0, 0), 42));
871 QVERIFY_SQL(model, setData(model.index(0, 1), "conflict"));
872 QFAIL_SQL(model, submitAll());
873
874 // failed insert is still cached
875 QCOMPARE(model.rowCount(), 2);
876 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("conflict"));
877 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("blah"));
878
879 // cached insert affects subsequent operations
880 values.setValue(i: 1, val: QString("spam"));
881 if (submitpolicy != QSqlTableModel::OnManualSubmit) {
882 QFAIL_SQL(model, setData(model.index(1, 1), QString("eggs")));
883 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("blah"));
884 QFAIL_SQL(model, setRecord(1, values));
885 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("blah"));
886 QFAIL_SQL(model, insertRow(2));
887 QCOMPARE(model.rowCount(), 2);
888 QFAIL_SQL(model, removeRow(1));
889 QCOMPARE(model.rowCount(), 2);
890 } else {
891 QVERIFY_SQL(model, setData(model.index(1, 1), QString("eggs")));
892 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("eggs"));
893 QVERIFY_SQL(model, setRecord(1, values));
894 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("spam"));
895 QVERIFY_SQL(model, insertRow(2));
896 QCOMPARE(model.rowCount(), 3);
897 QVERIFY_SQL(model, removeRow(1));
898 QCOMPARE(model.rowCount(), 3);
899 }
900
901 // restore empty table
902 model.revertAll();
903 QVERIFY_SQL(model, removeRow(0));
904 QVERIFY_SQL(model, submitAll());
905 QVERIFY_SQL(model, select());
906 QCOMPARE(model.rowCount(), 0);
907}
908
909void tst_QSqlTableModel::insertRecord()
910{
911 QFETCH(QString, dbName);
912 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
913 CHECK_DATABASE(db);
914
915 QSqlTableModel model(0, db);
916 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
917 model.setTable(test);
918 model.setSort(column: 0, order: Qt::AscendingOrder);
919 QVERIFY_SQL(model, select());
920
921 QSqlRecord rec = model.record();
922 rec.setValue(i: 0, val: 42);
923 rec.setValue(i: 1, val: QString("vohi"));
924 rec.setValue(i: 2, val: 1);
925 QVERIFY(model.insertRecord(1, rec));
926 QCOMPARE(model.rowCount(), 4);
927
928 QCOMPARE(model.data(model.index(1, 0)).toInt(), 42);
929 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("vohi"));
930 QCOMPARE(model.data(model.index(1, 2)).toInt(), 1);
931
932 model.revertAll();
933 model.setEditStrategy(QSqlTableModel::OnRowChange);
934
935 QVERIFY(model.insertRecord(-1, rec));
936
937 QCOMPARE(model.data(model.index(3, 0)).toInt(), 42);
938 QCOMPARE(model.data(model.index(3, 1)).toString(), QString("vohi"));
939 QCOMPARE(model.data(model.index(3, 2)).toInt(), 1);
940}
941
942void tst_QSqlTableModel::insertMultiRecords()
943{
944 QFETCH(QString, dbName);
945 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
946 CHECK_DATABASE(db);
947
948 QSqlTableModel model(0, db);
949 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
950 model.setTable(test);
951 model.setSort(column: 0, order: Qt::AscendingOrder);
952 QVERIFY_SQL(model, select());
953
954 QCOMPARE(model.rowCount(), 3);
955
956 QVERIFY(model.insertRow(2));
957
958 QCOMPARE(model.data(model.index(2, 0)), QVariant(model.record().field(0).type()));
959 QCOMPARE(model.data(model.index(2, 1)), QVariant(model.record().field(1).type()));
960 QCOMPARE(model.data(model.index(2, 2)), QVariant(model.record().field(2).type()));
961
962 QVERIFY(model.insertRow(3));
963 QVERIFY(model.insertRow(0));
964
965 QCOMPARE(model.data(model.index(5, 0)).toInt(), 3);
966 QCOMPARE(model.data(model.index(5, 1)).toString(), QString("vohi"));
967 QCOMPARE(model.data(model.index(5, 2)).toInt(), 3);
968
969 QVERIFY(model.setData(model.index(0, 0), QVariant(42)));
970 QVERIFY(model.setData(model.index(3, 0), QVariant(43)));
971 QVERIFY(model.setData(model.index(4, 0), QVariant(44)));
972 QVERIFY(model.setData(model.index(4, 1), QVariant(QLatin1String("gunnar"))));
973 QVERIFY(model.setData(model.index(4, 2), QVariant(1)));
974
975 QVERIFY(model.submitAll());
976 model.clear();
977 model.setTable(test);
978 model.setSort(column: 0, order: Qt::AscendingOrder);
979 QVERIFY_SQL(model, select());
980
981 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
982 QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
983 QCOMPARE(model.data(model.index(2, 0)).toInt(), 3);
984 QCOMPARE(model.data(model.index(3, 0)).toInt(), 42);
985 QCOMPARE(model.data(model.index(4, 0)).toInt(), 43);
986 QCOMPARE(model.data(model.index(5, 0)).toInt(), 44);
987 QCOMPARE(model.data(model.index(5, 1)).toString(), QString("gunnar"));
988 QCOMPARE(model.data(model.index(5, 2)).toInt(), 1);
989}
990
991void tst_QSqlTableModel::insertWithAutoColumn()
992{
993 QFETCH(QString, dbName);
994 QFETCH(int, submitpolicy_i);
995 QSqlTableModel::EditStrategy submitpolicy = (QSqlTableModel::EditStrategy) submitpolicy_i;
996 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
997 CHECK_DATABASE(db);
998
999 QString tbl = qTableName(prefix: "autoColumnTest", __FILE__, db);
1000 QSqlQuery q(db);
1001 q.exec(query: "DROP TABLE " + tbl);
1002 QVERIFY_SQL(q, exec("CREATE TABLE " + tbl + "(id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT)"));
1003
1004 QSqlTableModel model(0, db);
1005 model.setTable(tbl);
1006 model.setSort(column: 0, order: Qt::AscendingOrder);
1007 model.setEditStrategy(submitpolicy);
1008
1009 QVERIFY_SQL(model, select());
1010 QCOMPARE(model.rowCount(), 0);
1011
1012 // For insertRow/insertRows, we have to touch at least one column
1013 // or else the generated flag won't be set, which would lead to
1014 // an empty column list in the INSERT statement, which generally
1015 // does not work.
1016 if (submitpolicy != QSqlTableModel::OnManualSubmit) {
1017 for (int id = 1; id <= 2; ++id) {
1018 QVERIFY_SQL(model, insertRow(0));
1019 QVERIFY_SQL(model, setData(model.index(0, 1), QString("foo")));
1020 QVERIFY_SQL(model, submit());
1021 QCOMPARE(model.data(model.index(0, 0)).toInt(), id);
1022 }
1023 } else {
1024 QVERIFY_SQL(model, insertRows(0, 2));
1025 QVERIFY_SQL(model, setData(model.index(0, 1), QString("foo")));
1026 QVERIFY_SQL(model, setData(model.index(1, 1), QString("foo")));
1027 }
1028
1029 QCOMPARE(model.rowCount(), 2);
1030
1031 QSqlRecord rec = db.record(tablename: tbl);
1032 QVERIFY(rec.field(0).isAutoValue());
1033 rec.setGenerated(i: 0, generated: false);
1034
1035 QVERIFY_SQL(model, insertRecord(0, rec));
1036 if (submitpolicy != QSqlTableModel::OnManualSubmit)
1037 QCOMPARE(model.data(model.index(0, 0)).toInt(), 3);
1038
1039 QCOMPARE(model.rowCount(), 3);
1040
1041 if (submitpolicy != QSqlTableModel::OnManualSubmit) {
1042 // Rows updated in original positions after previous submits.
1043 QCOMPARE(model.data(model.index(0, 0)).toInt(), 3);
1044 QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
1045 QCOMPARE(model.data(model.index(2, 0)).toInt(), 1);
1046 } else {
1047 // Manual submit is followed by requery.
1048 QVERIFY_SQL(model, submitAll());
1049 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
1050 QCOMPARE(model.data(model.index(1, 0)).toInt(), 2);
1051 QCOMPARE(model.data(model.index(2, 0)).toInt(), 3);
1052 }
1053
1054 QVERIFY_SQL(q, exec("DROP TABLE " + tbl));
1055}
1056
1057void tst_QSqlTableModel::submitAll()
1058{
1059 QFETCH(QString, dbName);
1060 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1061 CHECK_DATABASE(db);
1062
1063 QSqlTableModel model(0, db);
1064 model.setTable(test);
1065 model.setSort(column: 0, order: Qt::AscendingOrder);
1066 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
1067 QVERIFY_SQL(model, select());
1068
1069 QVERIFY(model.setData(model.index(0, 1), "harry2", Qt::EditRole));
1070 QVERIFY(model.setData(model.index(1, 1), "trond2", Qt::EditRole));
1071
1072 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry2"));
1073 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond2"));
1074
1075 QVERIFY_SQL(model, submitAll());
1076
1077 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry2"));
1078 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond2"));
1079
1080 QVERIFY(model.setData(model.index(0, 1), "harry", Qt::EditRole));
1081 QVERIFY(model.setData(model.index(1, 1), "trond", Qt::EditRole));
1082
1083 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1084 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
1085
1086 QVERIFY_SQL(model, submitAll());
1087
1088 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1089 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
1090}
1091
1092void tst_QSqlTableModel::removeRow()
1093{
1094 QFETCH(QString, dbName);
1095 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1096 CHECK_DATABASE(db);
1097
1098 QSqlTableModel model(0, db);
1099 model.setTable(test);
1100 model.setSort(column: 0, order: Qt::AscendingOrder);
1101 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
1102 QVERIFY_SQL(model, select());
1103 QCOMPARE(model.rowCount(), 3);
1104
1105 // headerDataChanged must be emitted by the model since the row won't vanish until select
1106 qRegisterMetaType<Qt::Orientation>(typeName: "Qt::Orientation");
1107 QSignalSpy headerDataChangedSpy(&model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)));
1108
1109 QVERIFY(model.removeRow(1));
1110 QCOMPARE(headerDataChangedSpy.count(), 1);
1111 QCOMPARE(*static_cast<const Qt::Orientation *>(headerDataChangedSpy.at(0).value(0).constData()), Qt::Vertical);
1112 QCOMPARE(headerDataChangedSpy.at(0).at(1).toInt(), 1);
1113 QCOMPARE(headerDataChangedSpy.at(0).at(2).toInt(), 1);
1114 QVERIFY(model.submitAll());
1115 QCOMPARE(model.rowCount(), 2);
1116
1117 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
1118 QCOMPARE(model.data(model.index(1, 0)).toInt(), 3);
1119 model.clear();
1120
1121 recreateTestTables();
1122
1123 model.setTable(test);
1124 model.setEditStrategy(QSqlTableModel::OnRowChange);
1125 QVERIFY_SQL(model, select());
1126 QCOMPARE(model.rowCount(), 3);
1127
1128 headerDataChangedSpy.clear();
1129 QVERIFY(model.removeRow(1));
1130 QCOMPARE(headerDataChangedSpy.count(), 1);
1131 QCOMPARE(model.rowCount(), 3);
1132
1133 QVERIFY_SQL(model, select());
1134 QCOMPARE(model.rowCount(), 2);
1135
1136 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1137 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("vohi"));
1138}
1139
1140void tst_QSqlTableModel::removeRows()
1141{
1142 QFETCH(QString, dbName);
1143 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1144 CHECK_DATABASE(db);
1145
1146 QSqlTableModel model(0, db);
1147 model.setTable(test);
1148 model.setSort(column: 0, order: Qt::AscendingOrder);
1149 model.setEditStrategy(QSqlTableModel::OnFieldChange);
1150 QVERIFY_SQL(model, select());
1151 QCOMPARE(model.rowCount(), 3);
1152
1153 QSignalSpy beforeDeleteSpy(&model, SIGNAL(beforeDelete(int)));
1154
1155 // Make sure wrong stuff is ok
1156 QVERIFY(!model.removeRows(-1,1)); // negative start
1157 QVERIFY(!model.removeRows(-1, 0)); // negative start, and zero count
1158 QVERIFY(!model.removeRows(1, 0)); // zero count
1159 QVERIFY(!model.removeRows(5, 1)); // past end (DOESN'T causes a beforeDelete to be emitted)
1160 QVERIFY(!model.removeRows(1, 0, model.index(2, 0))); // can't pass a valid modelindex
1161 QFAIL_SQL(model, removeRows(0, 2)); // more than 1 row on OnFieldChange
1162
1163 QVERIFY_SQL(model, removeRows(0, 1));
1164 QVERIFY_SQL(model, removeRows(1, 1));
1165 QCOMPARE(beforeDeleteSpy.count(), 2);
1166 QCOMPARE(beforeDeleteSpy.at(0).at(0).toInt(), 0);
1167 QCOMPARE(beforeDeleteSpy.at(1).at(0).toInt(), 1);
1168 // deleted rows shown as empty until select
1169 QCOMPARE(model.rowCount(), 3);
1170 QCOMPARE(model.data(model.index(0, 1)).toString(), QString(""));
1171 QVERIFY(model.select());
1172 // deleted rows are gone
1173 QCOMPARE(model.rowCount(), 1);
1174 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("vohi"));
1175 model.clear();
1176
1177 recreateTestTables();
1178 model.setTable(test);
1179 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
1180 QVERIFY_SQL(model, select());
1181 QCOMPARE(model.rowCount(), 3);
1182 beforeDeleteSpy.clear();
1183
1184 // When the edit strategy is OnManualSubmit the beforeDelete() signal
1185 // isn't emitted until submitAll() is called.
1186
1187 QVERIFY(!model.removeRows(-1,1)); // negative start
1188 QVERIFY(!model.removeRows(-1, 0)); // negative start, and zero count
1189 QVERIFY(!model.removeRows(1, 0)); // zero count
1190 QVERIFY(!model.removeRows(5, 1)); // past end (DOESN'T cause a beforeDelete to be emitted)
1191 QVERIFY(!model.removeRows(1, 0, model.index(2, 0))); // can't pass a valid modelindex
1192
1193 qRegisterMetaType<Qt::Orientation>(typeName: "Qt::Orientation");
1194 QSignalSpy headerDataChangedSpy(&model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)));
1195 QVERIFY(model.removeRows(0, 2, QModelIndex()));
1196 QCOMPARE(headerDataChangedSpy.count(), 2);
1197 QCOMPARE(headerDataChangedSpy.at(0).at(1).toInt(), 1);
1198 QCOMPARE(headerDataChangedSpy.at(0).at(2).toInt(), 1);
1199 QCOMPARE(headerDataChangedSpy.at(1).at(1).toInt(), 0);
1200 QCOMPARE(headerDataChangedSpy.at(1).at(2).toInt(), 0);
1201 QCOMPARE(model.rowCount(), 3);
1202 QCOMPARE(beforeDeleteSpy.count(), 0);
1203 QVERIFY(model.submitAll());
1204 QCOMPARE(beforeDeleteSpy.count(), 2);
1205 QCOMPARE(beforeDeleteSpy.at(0).at(0).toInt(), 0);
1206 QCOMPARE(beforeDeleteSpy.at(1).at(0).toInt(), 1);
1207 QCOMPARE(model.rowCount(), 1);
1208 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("vohi"));
1209}
1210
1211void tst_QSqlTableModel::removeInsertedRow()
1212{
1213 QFETCH(QString, dbName);
1214 QFETCH(int, submitpolicy_i);
1215 QSqlTableModel::EditStrategy submitpolicy = (QSqlTableModel::EditStrategy) submitpolicy_i;
1216 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1217 CHECK_DATABASE(db);
1218
1219 QSqlTableModel model(0, db);
1220 model.setTable(test);
1221 model.setSort(column: 0, order: Qt::AscendingOrder);
1222
1223 model.setEditStrategy(submitpolicy);
1224 QVERIFY_SQL(model, select());
1225 QCOMPARE(model.rowCount(), 3);
1226
1227 QVERIFY(model.insertRow(1));
1228 QCOMPARE(model.rowCount(), 4);
1229
1230 QVERIFY(model.removeRow(1));
1231 QCOMPARE(model.rowCount(), 3);
1232
1233 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1234 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
1235 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
1236
1237 // Now insert a row with a null, and check that removing it also works (QTBUG-15979 etc)
1238 model.insertRow(arow: 1);
1239 model.setData(index: model.index(row: 1,column: 0), value: 55);
1240 model.setData(index: model.index(row: 1,column: 1), value: QString("null columns"));
1241 model.setData(index: model.index(row: 1,column: 2), value: QVariant());
1242
1243 model.submitAll();
1244
1245 if (model.editStrategy() != QSqlTableModel::OnManualSubmit) {
1246 QCOMPARE(model.rowCount(), 4);
1247 QCOMPARE(model.data(model.index(1, 0)).toInt(), 55);
1248 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("null columns"));
1249 QCOMPARE(model.data(model.index(1, 2)).isNull(), true);
1250 QVERIFY(model.select());
1251 }
1252
1253 QCOMPARE(model.rowCount(), 4);
1254 QCOMPARE(model.data(model.index(3, 0)).toInt(), 55);
1255 QCOMPARE(model.data(model.index(3, 1)).toString(), QString("null columns"));
1256 QCOMPARE(model.data(model.index(3, 2)).isNull(), true);
1257
1258 QVERIFY(model.removeRow(3));
1259 model.submitAll();
1260
1261 if (model.editStrategy() != QSqlTableModel::OnManualSubmit) {
1262 QCOMPARE(model.rowCount(), 4);
1263 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1264 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
1265 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
1266 QCOMPARE(model.data(model.index(3, 1)).toString(), QString(""));
1267 QVERIFY(model.select());
1268 }
1269
1270 QCOMPARE(model.rowCount(), 3);
1271 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1272 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
1273 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
1274}
1275
1276void tst_QSqlTableModel::removeInsertedRows()
1277{
1278 QFETCH(QString, dbName);
1279 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1280 CHECK_DATABASE(db);
1281
1282 QSqlTableModel model(0, db);
1283 model.setTable(test);
1284 model.setSort(column: 0, order: Qt::AscendingOrder);
1285 model.setEditStrategy(QSqlTableModel::OnManualSubmit); // you can't insert more than one row otherwise
1286 QVERIFY_SQL(model, select());
1287 QCOMPARE(model.rowCount(), 3);
1288
1289 // First put two empty rows, and remove them one by one
1290 QVERIFY(model.insertRows(1, 2));
1291 QCOMPARE(model.rowCount(), 5);
1292 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1293 QCOMPARE(model.data(model.index(1, 1)).toString(), QString());
1294 QCOMPARE(model.data(model.index(2, 1)).toString(), QString());
1295 QCOMPARE(model.data(model.index(3, 1)).toString(), QString("trond"));
1296 QCOMPARE(model.data(model.index(4, 1)).toString(), QString("vohi"));
1297
1298 QVERIFY(model.removeRow(1));
1299 QCOMPARE(model.rowCount(), 4);
1300
1301 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1302 QCOMPARE(model.data(model.index(1, 1)).toString(), QString());
1303 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("trond"));
1304 QCOMPARE(model.data(model.index(3, 1)).toString(), QString("vohi"));
1305
1306 QVERIFY(model.removeRow(1));
1307 QCOMPARE(model.rowCount(), 3);
1308 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1309 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
1310 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
1311
1312 // Now put two empty rows, and remove them all at once
1313 QVERIFY(model.insertRows(1, 2));
1314 QCOMPARE(model.rowCount(), 5);
1315 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1316 QCOMPARE(model.data(model.index(1, 1)).toString(), QString());
1317 QCOMPARE(model.data(model.index(2, 1)).toString(), QString());
1318 QCOMPARE(model.data(model.index(3, 1)).toString(), QString("trond"));
1319 QCOMPARE(model.data(model.index(4, 1)).toString(), QString("vohi"));
1320
1321 QVERIFY(model.removeRows(1, 2));
1322 QCOMPARE(model.rowCount(), 3);
1323 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1324 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond"));
1325 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi"));
1326
1327
1328 // Now put two empty rows, and remove one good and two empty
1329 QVERIFY(model.insertRows(1, 2));
1330 QCOMPARE(model.rowCount(), 5);
1331 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1332 QCOMPARE(model.data(model.index(1, 1)).toString(), QString());
1333 QCOMPARE(model.data(model.index(2, 1)).toString(), QString());
1334 QCOMPARE(model.data(model.index(3, 1)).toString(), QString("trond"));
1335 QCOMPARE(model.data(model.index(4, 1)).toString(), QString("vohi"));
1336
1337 QVERIFY(model.removeRows(0, 3));
1338 QVERIFY(model.submitAll()); // otherwise the remove of the real row doesn't work
1339
1340 QCOMPARE(model.rowCount(), 2);
1341 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("trond"));
1342 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("vohi"));
1343
1344 // Reset back again
1345 model.clear();
1346 recreateTestTables();
1347 model.setTable(test);
1348 model.setSort(column: 0, order: Qt::AscendingOrder);
1349 model.setEditStrategy(QSqlTableModel::OnManualSubmit); // you can't insert more than one row otherwise
1350 QVERIFY_SQL(model, select());
1351 QCOMPARE(model.rowCount(), 3);
1352
1353 // Now two empty and one good
1354 QVERIFY(model.insertRows(1, 2));
1355 QCOMPARE(model.rowCount(), 5);
1356 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1357 QCOMPARE(model.data(model.index(1, 1)).toString(), QString());
1358 QCOMPARE(model.data(model.index(2, 1)).toString(), QString());
1359 QCOMPARE(model.data(model.index(3, 1)).toString(), QString("trond"));
1360 QCOMPARE(model.data(model.index(4, 1)).toString(), QString("vohi"));
1361
1362 QVERIFY(model.removeRows(1, 3));
1363 QVERIFY(model.submitAll());
1364 QCOMPARE(model.rowCount(), 2);
1365 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1366 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("vohi"));
1367
1368 // Reset back again
1369 model.clear();
1370 recreateTestTables();
1371 model.setTable(test);
1372 model.setSort(column: 0, order: Qt::AscendingOrder);
1373 model.setEditStrategy(QSqlTableModel::OnManualSubmit); // you can't insert more than one row otherwise
1374 QVERIFY_SQL(model, select());
1375 QCOMPARE(model.rowCount(), 3);
1376
1377 // one empty, one good, one empty
1378 QVERIFY(model.insertRows(1, 1));
1379 QVERIFY(model.insertRows(3, 1));
1380 QCOMPARE(model.rowCount(), 5);
1381 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1382 QCOMPARE(model.data(model.index(1, 1)).toString(), QString());
1383 QCOMPARE(model.data(model.index(2, 1)).toString(), QString("trond"));
1384 QCOMPARE(model.data(model.index(3, 1)).toString(), QString());
1385 QCOMPARE(model.data(model.index(4, 1)).toString(), QString("vohi"));
1386
1387 QVERIFY(model.removeRows(1, 3));
1388 QVERIFY(model.submitAll());
1389 QCOMPARE(model.rowCount(), 2);
1390 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1391 QCOMPARE(model.data(model.index(1, 1)).toString(), QString("vohi"));
1392}
1393
1394void tst_QSqlTableModel::revert()
1395{
1396 QFETCH(QString, dbName);
1397 QFETCH(int, submitpolicy_i);
1398 QSqlTableModel::EditStrategy submitpolicy = (QSqlTableModel::EditStrategy) submitpolicy_i;
1399 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1400 CHECK_DATABASE(db);
1401
1402 QString tblA = qTableName(prefix: "revertATest", __FILE__, db);
1403 QString tblB = qTableName(prefix: "revertBTest", __FILE__, db);
1404 QSqlQuery q(db);
1405 q.exec(query: "PRAGMA foreign_keys = ON;");
1406 q.exec(query: "DROP TABLE " + tblB);
1407 q.exec(query: "DROP TABLE " + tblA);
1408 QVERIFY_SQL(q, exec("CREATE TABLE " + tblA + "(a INT PRIMARY KEY)"));
1409 QVERIFY_SQL(q, exec("CREATE TABLE " + tblB + "(b INT PRIMARY KEY, FOREIGN KEY (b) REFERENCES " + tblA + " (a))"));
1410 QVERIFY_SQL(q, exec("INSERT INTO " + tblA + "(a) VALUES (1)"));
1411 QVERIFY_SQL(q, exec("INSERT INTO " + tblB + "(b) VALUES (1)"));
1412 if (q.exec(query: "UPDATE " + tblA + " SET a = -1"))
1413 QSKIP("database does not enforce foreign key constraints, skipping test");
1414
1415 QSqlTableModel model(0, db);
1416 model.setTable(tblA);
1417 model.setSort(column: 0, order: Qt::AscendingOrder);
1418 model.setEditStrategy(submitpolicy);
1419
1420 QVERIFY_SQL(model, select());
1421 QCOMPARE(model.rowCount(), 1);
1422 QFAIL_SQL(model, isDirty());
1423
1424 // don't crash if there is no change
1425 model.revert();
1426
1427 // UPDATE
1428 // invalid value makes submit fail leaving pending update in cache
1429 const QModelIndex idx = model.index(row: 0, column: 0);
1430 if (submitpolicy == QSqlTableModel::OnFieldChange)
1431 QFAIL_SQL(model, setData(idx, int(-1)));
1432 else
1433 QVERIFY_SQL(model, setData(idx, int(-1)));
1434 QVERIFY_SQL(model, isDirty(idx));
1435 model.revert();
1436 if (submitpolicy != QSqlTableModel::OnManualSubmit)
1437 QFAIL_SQL(model, isDirty(idx));
1438 else
1439 QVERIFY_SQL(model, isDirty(idx));
1440
1441 // INSERT
1442 QVERIFY_SQL(model, select());
1443 // insertRow() does not submit leaving pending insert in cache
1444 QVERIFY_SQL(model, insertRow(0));
1445 QCOMPARE(model.rowCount(), 2);
1446 QVERIFY_SQL(model, isDirty());
1447 model.revert();
1448 if (submitpolicy != QSqlTableModel::OnManualSubmit)
1449 QFAIL_SQL(model, isDirty());
1450 else
1451 QVERIFY_SQL(model, isDirty());
1452
1453 // DELETE
1454 QVERIFY_SQL(model, select());
1455 // foreign key makes submit fail leaving pending delete in cache
1456 if (submitpolicy == QSqlTableModel::OnManualSubmit)
1457 QVERIFY_SQL(model, removeRow(0));
1458 else
1459 QFAIL_SQL(model, removeRow(0));
1460 QVERIFY_SQL(model, isDirty());
1461 model.revert();
1462 if (submitpolicy != QSqlTableModel::OnManualSubmit)
1463 QFAIL_SQL(model, isDirty());
1464 else
1465 QVERIFY_SQL(model, isDirty());
1466
1467 q.exec(query: "DROP TABLE " + tblB);
1468 q.exec(query: "DROP TABLE " + tblA);
1469}
1470
1471void tst_QSqlTableModel::isDirty()
1472{
1473 QFETCH(QString, dbName);
1474 QFETCH(int, submitpolicy_i);
1475 QSqlTableModel::EditStrategy submitpolicy = (QSqlTableModel::EditStrategy) submitpolicy_i;
1476 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1477 CHECK_DATABASE(db);
1478
1479 QSqlTableModel model(0, db);
1480 model.setEditStrategy(submitpolicy);
1481 model.setTable(test);
1482 QFAIL_SQL(model, isDirty());
1483
1484 model.setSort(column: 0, order: Qt::AscendingOrder);
1485 QVERIFY_SQL(model, select());
1486 QFAIL_SQL(model, isDirty());
1487
1488 // check that setting the current value does not add to the cache
1489 {
1490 QModelIndex i = model.index(row: 0, column: 1);
1491 QVariant v = model.data(idx: i, role: Qt::EditRole);
1492 QVERIFY_SQL(model, setData(i, v));
1493 QFAIL_SQL(model, isDirty());
1494 }
1495
1496 if (submitpolicy != QSqlTableModel::OnFieldChange) {
1497 // setData() followed by revertAll()
1498 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1499 QVERIFY_SQL(model, setData(model.index(0, 1), QString("sam i am")));
1500 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("sam i am"));
1501 QVERIFY_SQL(model, isDirty());
1502 QVERIFY_SQL(model, isDirty(model.index(0, 1)));
1503 model.revertAll();
1504 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1505 QFAIL_SQL(model, isDirty());
1506 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1507
1508 // setData() followed by select(), which clears changes
1509 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1510 QVERIFY_SQL(model, setData(model.index(0, 1), QString("sam i am")));
1511 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("sam i am"));
1512 QVERIFY_SQL(model, isDirty());
1513 QVERIFY_SQL(model, isDirty(model.index(0, 1)));
1514 QVERIFY_SQL(model, select());
1515 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1516 QFAIL_SQL(model, isDirty());
1517 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1518 }
1519
1520 if (submitpolicy == QSqlTableModel::OnRowChange) {
1521 // dirty row must block change on other rows
1522 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1523 QVERIFY(model.rowCount() > 1);
1524 QVERIFY_SQL(model, setData(model.index(0, 1), QString("sam i am")));
1525 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("sam i am"));
1526 QVERIFY_SQL(model, isDirty());
1527 QVERIFY_SQL(model, isDirty(model.index(0, 1)));
1528 QVERIFY(!(model.flags(model.index(1, 1)) & Qt::ItemIsEditable));
1529 QFAIL_SQL(model, setData(model.index(1, 1), QString("sam i am")));
1530 QFAIL_SQL(model, setRecord(1, model.record(1)));
1531 QFAIL_SQL(model, insertRow(1));
1532 QFAIL_SQL(model, removeRow(1));
1533 QFAIL_SQL(model, isDirty(model.index(1, 1)));
1534
1535 model.revertAll();
1536 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1537 QFAIL_SQL(model, isDirty());
1538 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1539 }
1540
1541 // setData() followed by submitAll()
1542 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1543 QVERIFY_SQL(model, setData(model.index(0, 1), QString("sam i am")));
1544 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("sam i am"));
1545 if (submitpolicy != QSqlTableModel::OnFieldChange) {
1546 QVERIFY_SQL(model, isDirty());
1547 QVERIFY_SQL(model, isDirty(model.index(0, 1)));
1548 }
1549 QVERIFY_SQL(model, submitAll());
1550 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("sam i am"));
1551 QFAIL_SQL(model, isDirty());
1552 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1553 // check status after refreshing underlying query
1554 QVERIFY_SQL(model, select());
1555 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("sam i am"));
1556 QFAIL_SQL(model, isDirty());
1557 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1558 //restore original state
1559 QVERIFY_SQL(model, setData(model.index(0, 1), QString("harry")));
1560 QVERIFY_SQL(model, submitAll());
1561 QVERIFY_SQL(model, select());
1562 QFAIL_SQL(model, isDirty());
1563 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1564
1565 QSqlRecord newvals = model.record(row: 0);
1566 newvals.setValue(i: 1, val: QString("sam i am"));
1567 newvals.setGenerated(i: 1, generated: true);
1568 if (submitpolicy == QSqlTableModel::OnManualSubmit) {
1569 // setRecord() followed by revertAll()
1570 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1571 QVERIFY_SQL(model, setRecord(0, newvals));
1572 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("sam i am"));
1573 QVERIFY_SQL(model, isDirty());
1574 QVERIFY_SQL(model, isDirty(model.index(0, 1)));
1575 model.revertAll();
1576 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1577 QFAIL_SQL(model, isDirty());
1578 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1579
1580 // setRecord() followed by select(), which clears changes
1581 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1582 QVERIFY_SQL(model, setRecord(0, newvals));
1583 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("sam i am"));
1584 QVERIFY_SQL(model, isDirty());
1585 QVERIFY_SQL(model, isDirty(model.index(0, 1)));
1586 QVERIFY_SQL(model, select());
1587 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1588 QFAIL_SQL(model, isDirty());
1589 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1590 }
1591
1592 // setRecord() followed by submitAll()
1593 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1594 QVERIFY_SQL(model, setRecord(0, newvals));
1595 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("sam i am"));
1596 if (submitpolicy == QSqlTableModel::OnManualSubmit) {
1597 QVERIFY_SQL(model, isDirty());
1598 QVERIFY_SQL(model, isDirty(model.index(0, 1)));
1599 }
1600 QVERIFY_SQL(model, submitAll());
1601 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("sam i am"));
1602 QFAIL_SQL(model, isDirty());
1603 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1604 // check status after refreshing underlying query
1605 QVERIFY_SQL(model, select());
1606 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("sam i am"));
1607 QFAIL_SQL(model, isDirty());
1608 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1609 //restore original state
1610 QVERIFY_SQL(model, setData(model.index(0, 1), QString("harry")));
1611 QVERIFY_SQL(model, submitAll());
1612 QVERIFY_SQL(model, select());
1613 QFAIL_SQL(model, isDirty());
1614 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1615
1616 // insertRow()
1617 QVERIFY_SQL(model, insertRow(0));
1618 QVERIFY_SQL(model, isDirty());
1619 QVERIFY_SQL(model, isDirty(model.index(0, 1)));
1620 model.revertAll();
1621 QFAIL_SQL(model, isDirty());
1622 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1623 QVERIFY_SQL(model, select());
1624 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1625 QFAIL_SQL(model, isDirty());
1626 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1627
1628 // removeRow()
1629 QSqlRecord saved_rec = model.record(row: 0);
1630 QVERIFY_SQL(model, removeRow(0));
1631 if (submitpolicy == QSqlTableModel::OnManualSubmit) {
1632 QVERIFY_SQL(model, isDirty());
1633 QVERIFY_SQL(model, isDirty(model.index(0, 1)));
1634 }
1635 QVERIFY_SQL(model, submitAll());
1636 QFAIL_SQL(model, isDirty());
1637 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1638 QVERIFY_SQL(model, select());
1639 QFAIL_SQL(model, isDirty());
1640 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1641 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("trond"));
1642
1643 // insertRecord(), put back the removed row
1644 for (int i = saved_rec.count() - 1; i >= 0; --i)
1645 saved_rec.setGenerated(i, generated: true);
1646 QVERIFY_SQL(model, insertRecord(0, saved_rec));
1647 if (submitpolicy == QSqlTableModel::OnManualSubmit) {
1648 QVERIFY_SQL(model, isDirty());
1649 QVERIFY_SQL(model, isDirty(model.index(0, 1)));
1650 }
1651 QVERIFY_SQL(model, submitAll());
1652 QFAIL_SQL(model, isDirty());
1653 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1654 QVERIFY_SQL(model, select());
1655 QFAIL_SQL(model, isDirty());
1656 QFAIL_SQL(model, isDirty(model.index(0, 1)));
1657 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry"));
1658}
1659
1660void tst_QSqlTableModel::emptyTable()
1661{
1662 QFETCH(QString, dbName);
1663 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1664 CHECK_DATABASE(db);
1665
1666 QSqlTableModel model(0, db);
1667 QCOMPARE(model.rowCount(), 0);
1668 QCOMPARE(model.columnCount(), 0);
1669
1670 model.setTable(qTableName(prefix: "emptytable", __FILE__, db));
1671 QCOMPARE(model.rowCount(), 0);
1672 QCOMPARE(model.columnCount(), 1);
1673
1674 QVERIFY_SQL(model, select());
1675 QCOMPARE(model.rowCount(), 0);
1676 QCOMPARE(model.columnCount(), 1);
1677
1678 // QTBUG-29108: check correct horizontal header for empty query with pending insert
1679 QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("id"));
1680 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
1681 model.insertRow(arow: 0);
1682 QCOMPARE(model.rowCount(), 1);
1683 QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("id"));
1684 model.revertAll();
1685}
1686
1687void tst_QSqlTableModel::tablesAndSchemas()
1688{
1689 QFETCH(QString, dbName);
1690 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1691 CHECK_DATABASE(db);
1692
1693 QSqlQuery q(db);
1694 q.exec(query: "DROP SCHEMA " + qTableName(prefix: "testschema", __FILE__, db) + " CASCADE");
1695 QVERIFY_SQL( q, exec("create schema " + qTableName("testschema", __FILE__, db)));
1696 QString tableName = qTableName(prefix: "testschema", __FILE__, db) + '.' + qTableName(prefix: "testtable", __FILE__, db);
1697 QVERIFY_SQL( q, exec("create table " + tableName + "(id int)"));
1698 QVERIFY_SQL( q, exec("insert into " + tableName + " values(1)"));
1699 QVERIFY_SQL( q, exec("insert into " + tableName + " values(2)"));
1700
1701 QSqlTableModel model(0, db);
1702 model.setTable(tableName);
1703 QVERIFY_SQL(model, select());
1704 QCOMPARE(model.rowCount(), 2);
1705 QCOMPARE(model.columnCount(), 1);
1706}
1707
1708void tst_QSqlTableModel::whitespaceInIdentifiers()
1709{
1710 QFETCH(QString, dbName);
1711 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1712 CHECK_DATABASE(db);
1713
1714 if (!testWhiteSpaceNames(name: db.driverName()))
1715 QSKIP("DBMS doesn't support whitespaces in identifiers");
1716
1717 QString tableName = qTableName(prefix: "qtestw hitespace", db);
1718
1719 QSqlTableModel model(0, db);
1720 model.setTable(tableName);
1721 QVERIFY_SQL(model, select());
1722}
1723
1724void tst_QSqlTableModel::primaryKeyOrder()
1725{
1726 QFETCH(QString, dbName);
1727 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1728 CHECK_DATABASE(db);
1729 QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
1730
1731 QSqlQuery q(db);
1732
1733 if (dbType == QSqlDriver::PostgreSQL)
1734 QVERIFY_SQL( q, exec("set client_min_messages='warning'"));
1735
1736 QVERIFY_SQL(q, exec("create table " + qTableName("foo", __FILE__, db) + "(a varchar(20), id int not null primary key, b varchar(20))"));
1737
1738 QSqlTableModel model(0, db);
1739 model.setTable(qTableName(prefix: "foo", __FILE__, db));
1740
1741 QSqlIndex pk = model.primaryKey();
1742 QCOMPARE(pk.count(), 1);
1743 QCOMPARE(pk.fieldName(0), QLatin1String("id"));
1744
1745 QVERIFY(model.insertRow(0));
1746 QVERIFY(model.setData(model.index(0, 0), "hello"));
1747 QVERIFY(model.setData(model.index(0, 1), 42));
1748 QVERIFY(model.setData(model.index(0, 2), "blah"));
1749 QVERIFY_SQL(model, submitAll());
1750
1751 QVERIFY(model.setData(model.index(0, 1), 43));
1752 QVERIFY_SQL(model, submitAll());
1753
1754 QCOMPARE(model.data(model.index(0, 1)).toInt(), 43);
1755}
1756
1757void tst_QSqlTableModel::setInvalidFilter()
1758{
1759 QFETCH(QString, dbName);
1760 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1761 CHECK_DATABASE(db);
1762
1763 // set an invalid filter, make sure it fails
1764 QSqlTableModel model(0, db);
1765 model.setTable(test);
1766 model.setFilter("blahfahsel");
1767
1768 QCOMPARE(model.filter(), QString("blahfahsel"));
1769 QVERIFY(!model.select());
1770
1771 // set a valid filter later, make sure if passes
1772 model.setFilter("id = 1");
1773 QVERIFY_SQL(model, select());
1774}
1775
1776void tst_QSqlTableModel::setFilter()
1777{
1778 QFETCH(QString, dbName);
1779 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1780 CHECK_DATABASE(db);
1781
1782 QSqlTableModel model(0, db);
1783 model.setTable(test);
1784 model.setFilter("id = 1");
1785 QCOMPARE(model.filter(), QString("id = 1"));
1786 QVERIFY_SQL(model, select());
1787
1788 QCOMPARE(model.rowCount(), 1);
1789 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
1790
1791 QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset()));
1792 QSignalSpy modelResetSpy(&model, SIGNAL(modelReset()));
1793 model.setFilter("id = 2");
1794
1795 // check the signals
1796 QCOMPARE(modelAboutToBeResetSpy.count(), 1);
1797 QCOMPARE(modelResetSpy.count(), 1);
1798
1799 QCOMPARE(model.rowCount(), 1);
1800 QCOMPARE(model.data(model.index(0, 0)).toInt(), 2);
1801}
1802
1803void tst_QSqlTableModel::sqlite_bigTable()
1804{
1805 QFETCH(QString, dbName);
1806 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1807 CHECK_DATABASE(db);
1808 const QString bigtable(qTableName(prefix: "bigtable", __FILE__, db));
1809
1810 bool hasTransactions = db.driver()->hasFeature(f: QSqlDriver::Transactions);
1811 if (hasTransactions) QVERIFY(db.transaction());
1812 QSqlQuery q(db);
1813 QVERIFY_SQL( q, exec("create table "+bigtable+"(id int primary key, name varchar)"));
1814 QVERIFY_SQL( q, prepare("insert into "+bigtable+"(id, name) values (?, ?)"));
1815 QElapsedTimer timing;
1816 timing.start();
1817 for (int i = 0; i < 10000; ++i) {
1818 q.addBindValue(val: i);
1819 q.addBindValue(val: QString::number(i));
1820 if (i % 1000 == 0 && timing.elapsed() > 5000)
1821 qDebug() << i << "records written";
1822 QVERIFY_SQL( q, exec());
1823 }
1824 q.clear();
1825 if (hasTransactions) QVERIFY(db.commit());
1826
1827 QSqlTableModel model(0, db);
1828 model.setTable(bigtable);
1829 QVERIFY_SQL(model, select());
1830
1831 QSqlRecord rec = model.record();
1832 rec.setValue(name: "id", val: 424242);
1833 rec.setValue(name: "name", val: "Guillaume");
1834 QVERIFY_SQL(model, insertRecord(-1, rec));
1835
1836 model.clear();
1837}
1838
1839// For task 118547: couldn't insert records unless select()
1840// had first been called.
1841void tst_QSqlTableModel::insertRecordBeforeSelect()
1842{
1843 QFETCH(QString, dbName);
1844 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1845 CHECK_DATABASE(db);
1846
1847 QSqlTableModel model(0, db);
1848 model.setTable(test);
1849 QCOMPARE(model.lastError().type(), QSqlError::NoError);
1850
1851 QSqlRecord buffer = model.record();
1852 buffer.setValue(name: "id", val: 13);
1853 buffer.setValue(name: "name", val: QString("The Lion King"));
1854 buffer.setValue(name: "title", val: 0);
1855 QVERIFY_SQL(model, insertRecord(-1, buffer));
1856
1857 buffer.setValue(name: "id", val: 26);
1858 buffer.setValue(name: "name", val: QString("T. Leary"));
1859 buffer.setValue(name: "title", val: 0);
1860 QVERIFY_SQL(model, insertRecord(1, buffer));
1861
1862 if (model.editStrategy() != QSqlTableModel::OnManualSubmit) {
1863 QCOMPARE(model.rowCount(), 2);
1864 QVERIFY_SQL(model, select());
1865 }
1866
1867 int rowCount = model.rowCount();
1868 model.clear();
1869 QCOMPARE(model.rowCount(), 0);
1870
1871 QSqlTableModel model2(0, db);
1872 model2.setTable(test);
1873 QVERIFY_SQL(model2, select());
1874 QCOMPARE(model2.rowCount(), rowCount);
1875}
1876
1877// For task 118547: set errors if table doesn't exist and if records
1878// are inserted and submitted on a non-existing table.
1879void tst_QSqlTableModel::submitAllOnInvalidTable()
1880{
1881 QFETCH(QString, dbName);
1882 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1883 CHECK_DATABASE(db);
1884
1885 QSqlTableModel model(0, db);
1886 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
1887
1888 // setTable returns a void, so the error can only be caught by
1889 // manually checking lastError(). ### Qt5: This should be changed!
1890 model.setTable(qTableName(prefix: "invalidTable", __FILE__, db));
1891 QCOMPARE(model.lastError().type(), QSqlError::StatementError);
1892
1893 // This will give us an empty record which is expected behavior
1894 QSqlRecord buffer = model.record();
1895 buffer.setValue(name: "bogus", val: 1000);
1896 buffer.setValue(name: "bogus2", val: QString("I will go nowhere!"));
1897
1898 // Inserting the record into the *model* will work (OnManualSubmit)
1899 QVERIFY_SQL(model, insertRecord(-1, buffer));
1900
1901 // The submit and select shall fail because the table doesn't exist
1902 QEXPECT_FAIL("", "The table doesn't exist: submitAll() shall fail",
1903 Continue);
1904 QVERIFY_SQL(model, submitAll());
1905 QEXPECT_FAIL("", "The table doesn't exist: select() shall fail",
1906 Continue);
1907 QVERIFY_SQL(model, select());
1908}
1909
1910// For task 147575: the rowsRemoved signal emitted from the model was lying
1911void tst_QSqlTableModel::insertRecordsInLoop()
1912{
1913 QFETCH(QString, dbName);
1914 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1915 CHECK_DATABASE(db);
1916
1917 QSqlTableModel model(0, db);
1918 model.setTable(test);
1919 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
1920 model.select();
1921
1922 QSqlRecord record = model.record();
1923 record.setValue(i: 0, val: 10);
1924 record.setValue(i: 1, val: "Testman");
1925 record.setValue(i: 2, val: 1);
1926
1927 QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset()));
1928 QSignalSpy modelResetSpy(&model, SIGNAL(modelReset()));
1929 QSignalSpy spyRowsInserted(&model, SIGNAL(rowsInserted(QModelIndex,int,int)));
1930 for (int i = 0; i < 10; i++) {
1931 QVERIFY(model.insertRecord(model.rowCount(), record));
1932 QCOMPARE(spyRowsInserted.at(i).at(1).toInt(), i+3); // The table already contains three rows
1933 QCOMPARE(spyRowsInserted.at(i).at(2).toInt(), i+3);
1934 }
1935 model.submitAll(); // submitAll() calls select() which clears and repopulates the table
1936
1937 // model emits reset signals
1938 QCOMPARE(modelAboutToBeResetSpy.count(), 1);
1939 QCOMPARE(modelResetSpy.count(), 1);
1940
1941 QCOMPARE(model.rowCount(), 13);
1942 QCOMPARE(model.columnCount(), 3);
1943}
1944
1945void tst_QSqlTableModel::sqlite_attachedDatabase()
1946{
1947 QFETCH(QString, dbName);
1948 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1949 CHECK_DATABASE(db);
1950 if(db.databaseName() == ":memory:")
1951 QSKIP(":memory: database, skipping test");
1952
1953 QSqlDatabase attachedDb = QSqlDatabase::cloneDatabase(other: db, connectionName: db.driverName() + QLatin1String("attached"));
1954 attachedDb.setDatabaseName(db.databaseName()+QLatin1String("attached.dat"));
1955 QVERIFY_SQL(attachedDb, open());
1956 QSqlQuery q(attachedDb);
1957 tst_Databases::safeDropTables(db: attachedDb, tableNames: QStringList() << "atest" << "atest2");
1958 QVERIFY_SQL( q, exec("CREATE TABLE atest(id int, text varchar(20))"));
1959 QVERIFY_SQL( q, exec("CREATE TABLE atest2(id int, text varchar(20))"));
1960 QVERIFY_SQL( q, exec("INSERT INTO atest VALUES(1, 'attached-atest')"));
1961 QVERIFY_SQL( q, exec("INSERT INTO atest2 VALUES(2, 'attached-atest2')"));
1962
1963 QSqlQuery q2(db);
1964 tst_Databases::safeDropTable(db, tableName: "atest");
1965 QVERIFY_SQL(q2, exec("CREATE TABLE atest(id int, text varchar(20))"));
1966 QVERIFY_SQL(q2, exec("INSERT INTO atest VALUES(3, 'main')"));
1967 QVERIFY_SQL(q2, exec("ATTACH DATABASE \""+attachedDb.databaseName()+"\" as adb"));
1968
1969 // This should query the table in the attached database (schema supplied)
1970 QSqlTableModel model(0, db);
1971 model.setTable("adb.atest");
1972 QVERIFY_SQL(model, select());
1973 QCOMPARE(model.rowCount(), 1);
1974 QCOMPARE(model.data(model.index(0, 0), Qt::DisplayRole).toInt(), 1);
1975 QCOMPARE(model.data(model.index(0, 1), Qt::DisplayRole).toString(), QLatin1String("attached-atest"));
1976
1977 // This should query the table in the attached database (unique tablename)
1978 model.setTable("atest2");
1979 QVERIFY_SQL(model, select());
1980 QCOMPARE(model.rowCount(), 1);
1981 QCOMPARE(model.data(model.index(0, 0), Qt::DisplayRole).toInt(), 2);
1982 QCOMPARE(model.data(model.index(0, 1), Qt::DisplayRole).toString(), QLatin1String("attached-atest2"));
1983
1984 // This should query the table in the main database (tables in main db has 1st priority)
1985 model.setTable("atest");
1986 QVERIFY_SQL(model, select());
1987 QCOMPARE(model.rowCount(), 1);
1988 QCOMPARE(model.data(model.index(0, 0), Qt::DisplayRole).toInt(), 3);
1989 QCOMPARE(model.data(model.index(0, 1), Qt::DisplayRole).toString(), QLatin1String("main"));
1990 attachedDb.close();
1991}
1992
1993
1994void tst_QSqlTableModel::tableModifyWithBlank()
1995{
1996 QFETCH(QString, dbName);
1997 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
1998 CHECK_DATABASE(db);
1999
2000 QSqlTableModel model(0, db);
2001 model.setTable(qTableName(prefix: "test4", __FILE__, db));
2002 model.select();
2003
2004 //generate a time stamp for the test. Add one second to the current time to make sure
2005 //it is different than the QSqlQuery test.
2006 QString timeString=QDateTime::currentDateTime().addSecs(secs: 1).toString(format: Qt::ISODate);
2007
2008 //insert a new row, with column0 being the timestamp.
2009 //Should be equivalent to QSqlQuery INSERT INTO... command)
2010 QVERIFY_SQL(model, insertRow(0));
2011 QVERIFY_SQL(model, setData(model.index(0,0),timeString));
2012 QVERIFY_SQL(model, submitAll());
2013
2014 //set a filter on the table so the only record we get is the one we just made
2015 //I could just do another setData command, but I want to make sure the TableModel
2016 //matches exactly what is stored in the database
2017 model.setFilter("column1='" + timeString + QLatin1Char('\'')); //filter to get just the newly entered row
2018 QVERIFY_SQL(model, select());
2019
2020 //Make sure we only get one record, and that it is the one we just made
2021 QCOMPARE(model.rowCount(), 1); //verify only one entry
2022 QCOMPARE(model.record(0).value(0).toString(), timeString); //verify correct record
2023
2024 //At this point we know that the initial value (timestamp) was succsefully stored in the database
2025 //Attempt to modify the data in the new record
2026 //equivalent to query.exec("update test set column3="... command in direct test
2027 //set the data in the first column to "col1ModelData"
2028 QVERIFY_SQL(model, setData(model.index(0,1), "col1ModelData"));
2029
2030 //do a quick check to make sure that the setData command properly set the value in the model
2031 QCOMPARE(model.record(0).value(1).toString(), QLatin1String("col1ModelData"));
2032
2033 //submit the changed data to the database
2034 //This is where I have been getting errors.
2035 QVERIFY_SQL(model, submitAll());
2036
2037 //make sure the model has the most current data for our record
2038 QVERIFY_SQL(model, select());
2039
2040 //verify that our new record was the only record returned
2041 QCOMPARE(model.rowCount(), 1);
2042
2043 //And that the record returned is, in fact, our test record.
2044 QCOMPARE(model.record(0).value(0).toString(), timeString);
2045
2046 //Make sure the value of the first column matches what we set it to previously.
2047 QCOMPARE(model.record(0).value(1).toString(), QLatin1String("col1ModelData"));
2048}
2049
2050void tst_QSqlTableModel::removeColumnAndRow()
2051{
2052 QFETCH(QString, dbName);
2053 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
2054 CHECK_DATABASE(db);
2055
2056 QSqlTableModel model(0, db);
2057 model.setTable(test);
2058 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
2059 QVERIFY_SQL(model, select());
2060 QCOMPARE(model.rowCount(), 3);
2061 QCOMPARE(model.columnCount(), 3);
2062
2063 QVERIFY(model.removeColumn(0));
2064 QVERIFY(model.removeRow(0));
2065 QVERIFY(model.submitAll());
2066 QCOMPARE(model.rowCount(), 2);
2067 QCOMPARE(model.columnCount(), 2);
2068
2069 // check with another table because the model has been modified
2070 // but not the sql table
2071 QSqlTableModel model2(0, db);
2072 model2.setTable(test);
2073 QVERIFY_SQL(model2, select());
2074 QCOMPARE(model2.rowCount(), 2);
2075 QCOMPARE(model2.columnCount(), 3);
2076}
2077
2078void tst_QSqlTableModel::insertBeforeDelete()
2079{
2080 QFETCH(QString, dbName);
2081 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
2082 CHECK_DATABASE(db);
2083
2084 QSqlQuery q(db);
2085 QVERIFY_SQL( q, exec("insert into " + test + " values(9, 'andrew', 9)"));
2086 QVERIFY_SQL( q, exec("insert into " + test + " values(10, 'justin', 10)"));
2087
2088 QSqlTableModel model(0, db);
2089 model.setTable(test);
2090 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
2091 QVERIFY_SQL(model, select());
2092
2093 QSqlRecord rec = model.record();
2094 rec.setValue(i: 0, val: 4);
2095 rec.setValue(i: 1, val: QString("bill"));
2096 rec.setValue(i: 2, val: 4);
2097 QVERIFY_SQL(model, insertRecord(4, rec));
2098
2099 QVERIFY_SQL(model, removeRow(5));
2100 QVERIFY_SQL(model, submitAll());
2101 QCOMPARE(model.rowCount(), 5);
2102}
2103
2104void tst_QSqlTableModel::invalidFilterAndHeaderData()
2105{
2106 QFETCH(QString, dbName);
2107 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
2108 CHECK_DATABASE(db);
2109
2110 QSqlTableModel model(0, db);
2111 model.setTable(test);
2112 model.setEditStrategy(QSqlTableModel::OnManualSubmit);
2113 QVERIFY_SQL(model, select());
2114 QVERIFY_SQL(model, setHeaderData(0, Qt::Horizontal, "id"));
2115 QVERIFY_SQL(model, setHeaderData(1, Qt::Horizontal, "name"));
2116 QVERIFY_SQL(model, setHeaderData(2, Qt::Horizontal, "title"));
2117
2118 model.setFilter("some nonsense");
2119
2120 QVariant v = model.headerData(section: 0, orientation: Qt::Horizontal, role: Qt::SizeHintRole);
2121 QVERIFY(!v.isValid());
2122}
2123
2124class SqlThread : public QThread
2125{
2126public:
2127 SqlThread() : QThread() {}
2128 void run()
2129 {
2130 QSqlDatabase db = QSqlDatabase::addDatabase(type: "QSQLITE", connectionName: "non-default-connection");
2131 QSqlTableModel stm(nullptr, db);
2132 isDone = true;
2133 }
2134 bool isDone = false;
2135};
2136
2137void tst_QSqlTableModel::modelInAnotherThread()
2138{
2139 oldHandler = qInstallMessageHandler(sqlTableModelMessageHandler);
2140 QSqlDatabase db = QSqlDatabase::addDatabase(type: "QSQLITE");
2141 CHECK_DATABASE(db);
2142 SqlThread t;
2143 t.start();
2144 QTRY_VERIFY(t.isDone);
2145 QVERIFY(t.isFinished());
2146}
2147
2148void tst_QSqlTableModel::sqlite_selectFromIdentifierWithDot()
2149{
2150 QFETCH(QString, dbName);
2151 QSqlDatabase db = QSqlDatabase::database(connectionName: dbName);
2152 CHECK_DATABASE(db);
2153 {
2154 const auto fieldDot = qTableName(prefix: "fieldDot", __FILE__, db);
2155 tst_Databases::safeDropTable(db, tableName: fieldDot);
2156 QSqlQuery qry(db);
2157 QVERIFY_SQL(qry, exec("create table " + fieldDot + " (id int primary key, "
2158 "\"person.firstname\" varchar(20))"));
2159 QVERIFY_SQL(qry, exec("insert into " + fieldDot + " values(1, 'Andy')"));
2160 QSqlTableModel model(0, db);
2161 model.setTable(fieldDot);
2162 QVERIFY_SQL(model, select());
2163 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
2164 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("Andy"));
2165 }
2166 const auto tableDot = QLatin1Char('[') + qTableName(prefix: "table.dot", __FILE__, db) + QLatin1Char(']');
2167 {
2168 tst_Databases::safeDropTable(db, tableName: tableDot);
2169 QSqlQuery qry(db);
2170 QVERIFY_SQL(qry, exec("create table " + tableDot + " (id int primary key, "
2171 "\"person.firstname\" varchar(20))"));
2172 QVERIFY_SQL(qry, exec("insert into " + tableDot + " values(1, 'Andy')"));
2173 QSqlTableModel model(0, db);
2174 model.setTable(tableDot);
2175 QVERIFY_SQL(model, select());
2176 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
2177 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("Andy"));
2178 }
2179 {
2180 QSqlDatabase attachedDb = QSqlDatabase::addDatabase(type: "QSQLITE", connectionName: "attachedDb");
2181 attachedDb.setDatabaseName(db.databaseName().replace(before: "foo.db", after: "attached.db"));
2182 QVERIFY(attachedDb.open());
2183 QSqlQuery qry(attachedDb);
2184 QVERIFY_SQL(qry, exec(QString("attach '%1' AS 'attached'").arg(db.databaseName())));
2185 QSqlTableModel model(0, attachedDb);
2186 model.setTable(QString("attached.%1").arg(a: tableDot));
2187 QVERIFY_SQL(model, select());
2188 QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
2189 QCOMPARE(model.data(model.index(0, 1)).toString(), QString("Andy"));
2190 }
2191}
2192
2193QTEST_MAIN(tst_QSqlTableModel)
2194#include "tst_qsqltablemodel.moc"
2195

source code of qtbase/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp