diff options
Diffstat (limited to 'tests/auto/sql/models')
10 files changed, 3588 insertions, 0 deletions
diff --git a/tests/auto/sql/models/models.pro b/tests/auto/sql/models/models.pro new file mode 100644 index 0000000000..d04ba01710 --- /dev/null +++ b/tests/auto/sql/models/models.pro @@ -0,0 +1,6 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qsqlquerymodel \ + qsqlrelationaltablemodel \ + qsqltablemodel \ + diff --git a/tests/auto/sql/models/qsqlquerymodel/.gitignore b/tests/auto/sql/models/qsqlquerymodel/.gitignore new file mode 100644 index 0000000000..e5ff8accf6 --- /dev/null +++ b/tests/auto/sql/models/qsqlquerymodel/.gitignore @@ -0,0 +1 @@ +tst_qsqlquerymodel diff --git a/tests/auto/sql/models/qsqlquerymodel/qsqlquerymodel.pro b/tests/auto/sql/models/qsqlquerymodel/qsqlquerymodel.pro new file mode 100644 index 0000000000..0664bae6e9 --- /dev/null +++ b/tests/auto/sql/models/qsqlquerymodel/qsqlquerymodel.pro @@ -0,0 +1,20 @@ +load(qttest_p4) +SOURCES += tst_qsqlquerymodel.cpp + +QT += widgets sql + +wince*: { + DEPLOYMENT_PLUGIN += qsqlite + LIBS += -lws2 +}else:symbian { + qt_not_deployed { + contains(S60_VERSION, 3.1)|contains(S60_VERSION, 3.2)|contains(S60_VERSION, 5.0) { + sqlite.path = /sys/bin + sqlite.files = sqlite3.dll + DEPLOYMENT += sqlite + } + } +} else { + win32:LIBS += -lws2_32 +} + diff --git a/tests/auto/sql/models/qsqlquerymodel/tst_qsqlquerymodel.cpp b/tests/auto/sql/models/qsqlquerymodel/tst_qsqlquerymodel.cpp new file mode 100644 index 0000000000..fabba48d7c --- /dev/null +++ b/tests/auto/sql/models/qsqlquerymodel/tst_qsqlquerymodel.cpp @@ -0,0 +1,641 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtGui> + +#include <qsqldriver.h> +#include <qsqldatabase.h> +#include <qsqlerror.h> +#include <qsqlfield.h> +#include <qsqlquery.h> +#include <qsqlrecord.h> + +#include <qsqlquerymodel.h> +#include <qsortfilterproxymodel.h> + +#include "../../kernel/qsqldatabase/tst_databases.h" + +//TESTED_CLASS= +//TESTED_FILES= + +Q_DECLARE_METATYPE(Qt::Orientation) + +class tst_QSqlQueryModel : public QObject +{ + Q_OBJECT + +public: + tst_QSqlQueryModel(); + virtual ~tst_QSqlQueryModel(); + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void insertColumn_data() { generic_data(); } + void insertColumn(); + void removeColumn_data() { generic_data(); } + void removeColumn(); + void record_data() { generic_data(); } + void record(); + void setHeaderData_data() { generic_data(); } + void setHeaderData(); + void fetchMore_data() { generic_data(); } + void fetchMore(); + + //problem specific tests + void withSortFilterProxyModel_data() { generic_data(); } + void withSortFilterProxyModel(); + void setQuerySignalEmission_data() { generic_data(); } + void setQuerySignalEmission(); + void setQueryWithNoRowsInResultSet_data() { generic_data(); } + void setQueryWithNoRowsInResultSet(); + + void task_180617(); + void task_180617_data() { generic_data(); } + void task_QTBUG_4963_setHeaderDataWithProxyModel(); + +private: + void generic_data(const QString &engine=QString()); + void dropTestTables(QSqlDatabase db); + void createTestTables(QSqlDatabase db); + void populateTestTables(QSqlDatabase db); + tst_Databases dbs; +}; + +/* Stupid class that makes protected members public for testing */ +class DBTestModel: public QSqlQueryModel +{ +public: + DBTestModel(QObject *parent = 0): QSqlQueryModel(parent) {} + QModelIndex indexInQuery(const QModelIndex &item) const { return QSqlQueryModel::indexInQuery(item); } +}; + +tst_QSqlQueryModel::tst_QSqlQueryModel() +{ +} + +tst_QSqlQueryModel::~tst_QSqlQueryModel() +{ +} + +void tst_QSqlQueryModel::initTestCase() +{ + qRegisterMetaType<QModelIndex>("QModelIndex"); + dbs.open(); + for (QStringList::ConstIterator it = dbs.dbNames.begin(); it != dbs.dbNames.end(); ++it) { + QSqlDatabase db = QSqlDatabase::database((*it)); + CHECK_DATABASE(db); + dropTestTables(db); //in case of leftovers + createTestTables(db); + populateTestTables(db); + } +} + +void tst_QSqlQueryModel::cleanupTestCase() +{ + for (QStringList::ConstIterator it = dbs.dbNames.begin(); it != dbs.dbNames.end(); ++it) { + QSqlDatabase db = QSqlDatabase::database((*it)); + CHECK_DATABASE(db); + dropTestTables(db); + } + dbs.close(); +} + +void tst_QSqlQueryModel::dropTestTables(QSqlDatabase db) +{ + QStringList tableNames; + tableNames << qTableName("test", __FILE__) + << qTableName("test2", __FILE__) + << qTableName("test3", __FILE__) + << qTableName("many", __FILE__); + tst_Databases::safeDropTables(db, tableNames); +} + +void tst_QSqlQueryModel::createTestTables(QSqlDatabase db) +{ + dropTestTables(db); + QSqlQuery q(db); + if(tst_Databases::isPostgreSQL(db)) + QVERIFY_SQL( q, exec("set client_min_messages='warning'")); + QVERIFY_SQL( q, exec("create table " + qTableName("test", __FILE__) + "(id integer not null, name varchar(20), title integer, primary key (id))")); + QVERIFY_SQL( q, exec("create table " + qTableName("test2", __FILE__) + "(id integer not null, title varchar(20), primary key (id))")); + QVERIFY_SQL( q, exec("create table " + qTableName("test3", __FILE__) + "(id integer not null, primary key (id))")); + QVERIFY_SQL( q, exec("create table " + qTableName("many", __FILE__) + "(id integer not null, name varchar(20), primary key (id))")); +} + +void tst_QSqlQueryModel::populateTestTables(QSqlDatabase db) +{ + qWarning() << "Populating test tables, this can take quite a while... ZZZzzz..."; + bool hasTransactions = db.driver()->hasFeature(QSqlDriver::Transactions); + + QSqlQuery q(db), q2(db); + + tst_Databases::safeDropTables(db, QStringList() << qTableName("manytmp", __FILE__) << qTableName("test3tmp", __FILE__)); + QVERIFY_SQL(q, exec("create table " + qTableName("manytmp", __FILE__) + "(id integer not null, name varchar(20), primary key (id))")); + QVERIFY_SQL(q, exec("create table " + qTableName("test3tmp", __FILE__) + "(id integer not null, primary key (id))")); + + if (hasTransactions) QVERIFY_SQL(db, transaction()); + + QVERIFY_SQL(q, exec("insert into " + qTableName("test", __FILE__) + " values(1, 'harry', 1)")); + QVERIFY_SQL(q, exec("insert into " + qTableName("test", __FILE__) + " values(2, 'trond', 2)")); + QVERIFY_SQL(q, exec("insert into " + qTableName("test2", __FILE__) + " values(1, 'herr')")); + QVERIFY_SQL(q, exec("insert into " + qTableName("test2", __FILE__) + " values(2, 'mister')")); + + QVERIFY_SQL(q, exec(QString("insert into " + qTableName("test3", __FILE__) + " values(0)"))); + QVERIFY_SQL(q, prepare("insert into "+qTableName("test3", __FILE__)+"(id) select id + ? from "+qTableName("test3tmp", __FILE__))); + for (int i=1; i<260; i*=2) { + q2.exec("delete from "+qTableName("test3tmp", __FILE__)); + QVERIFY_SQL(q2, exec("insert into "+qTableName("test3tmp", __FILE__)+"(id) select id from "+qTableName("test3", __FILE__))); + q.bindValue(0, i); + QVERIFY_SQL(q, exec()); + } + + QVERIFY_SQL(q, exec(QString("insert into " + qTableName("many", __FILE__) + "(id, name) values (0, \'harry\')"))); + QVERIFY_SQL(q, prepare("insert into "+qTableName("many", __FILE__)+"(id, name) select id + ?, name from "+qTableName("manytmp", __FILE__))); + for (int i=1; i < 2048; i*=2) { + q2.exec("delete from "+qTableName("manytmp", __FILE__)); + QVERIFY_SQL(q2, exec("insert into "+qTableName("manytmp", __FILE__)+"(id, name) select id, name from "+qTableName("many", __FILE__))); + q.bindValue(0, i); + QVERIFY_SQL(q, exec()); + } + + if (hasTransactions) QVERIFY_SQL(db, commit()); + + tst_Databases::safeDropTables(db, QStringList() << qTableName("manytmp", __FILE__) << qTableName("test3tmp", __FILE__)); +} + +void tst_QSqlQueryModel::generic_data(const QString& engine) +{ + if ( dbs.fillTestTable(engine) == 0 ) { + if(engine.isEmpty()) + QSKIP( "No database drivers are available in this Qt configuration", SkipAll ); + else + QSKIP( (QString("No database drivers of type %1 are available in this Qt configuration").arg(engine)).toLocal8Bit(), SkipAll ); + } +} + +void tst_QSqlQueryModel::init() +{ +} + +void tst_QSqlQueryModel::cleanup() +{ +} + +void tst_QSqlQueryModel::removeColumn() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + DBTestModel model; + model.setQuery(QSqlQuery("select * from " + qTableName("test", __FILE__), db)); + model.fetchMore(); + QSignalSpy spy(&model, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int))); + + QCOMPARE(model.columnCount(), 3); + QVERIFY(model.removeColumn(0)); + QCOMPARE(spy.count(), 1); + QVERIFY(*(QModelIndex *)spy.at(0).at(0).constData() == QModelIndex()); + QCOMPARE(spy.at(0).at(1).toInt(), 0); + QCOMPARE(spy.at(0).at(2).toInt(), 0); + + QCOMPARE(model.columnCount(), 2); + QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 1); + QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), 2); + QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1); + + QVERIFY(model.insertColumn(1)); + QCOMPARE(model.columnCount(), 3); + QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 1); + QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), 2); + QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1); + + QCOMPARE(model.data(model.index(0, 0)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 1)), QVariant()); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 3)), QVariant()); + + QVERIFY(!model.removeColumn(42)); + QVERIFY(!model.removeColumn(3)); + QVERIFY(!model.removeColumn(1, model.index(1, 2))); + QCOMPARE(model.columnCount(), 3); + + QVERIFY(model.removeColumn(2)); + + QCOMPARE(spy.count(), 2); + QVERIFY(*(QModelIndex *)spy.at(1).at(0).constData() == QModelIndex()); + QCOMPARE(spy.at(1).at(1).toInt(), 2); + QCOMPARE(spy.at(1).at(2).toInt(), 2); + + QCOMPARE(model.columnCount(), 2); + QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 1); + QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1); + + QVERIFY(model.removeColumn(1)); + + QCOMPARE(spy.count(), 3); + QVERIFY(*(QModelIndex *)spy.at(2).at(0).constData() == QModelIndex()); + QCOMPARE(spy.at(2).at(1).toInt(), 1); + QCOMPARE(spy.at(2).at(2).toInt(), 1); + + QCOMPARE(model.columnCount(), 1); + QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 1); + QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1); + QCOMPARE(model.data(model.index(0, 0)).toString(), QString("harry")); + + QVERIFY(model.removeColumn(0)); + + QCOMPARE(spy.count(), 4); + QVERIFY(*(QModelIndex *)spy.at(3).at(0).constData() == QModelIndex()); + QCOMPARE(spy.at(3).at(1).toInt(), 0); + QCOMPARE(spy.at(3).at(2).toInt(), 0); + + QCOMPARE(model.columnCount(), 0); + QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), -1); +} + +void tst_QSqlQueryModel::insertColumn() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + DBTestModel model; + model.setQuery(QSqlQuery("select * from " + qTableName("test", __FILE__), db)); + model.fetchMore(); // necessary??? + + bool isToUpper = db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2"); + const QString idColumn(isToUpper ? "ID" : "id"); + const QString nameColumn(isToUpper ? "NAME" : "name"); + const QString titleColumn(isToUpper ? "TITLE" : "title"); + + QSignalSpy spy(&model, SIGNAL(columnsInserted(QModelIndex, int, int))); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 3)), QVariant()); + + QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), idColumn); + QCOMPARE(model.headerData(1, Qt::Horizontal).toString(), nameColumn); + QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), titleColumn); + QCOMPARE(model.headerData(3, Qt::Horizontal).toString(), QString("4")); + + QVERIFY(model.insertColumn(1)); + + QCOMPARE(spy.count(), 1); + QVERIFY(*(QModelIndex *)spy.at(0).at(0).constData() == QModelIndex()); + QCOMPARE(spy.at(0).at(1).toInt(), 1); + QCOMPARE(spy.at(0).at(2).toInt(), 1); + + QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), 0); + QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), 1); + QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), 2); + QCOMPARE(model.indexInQuery(model.index(0, 4)).column(), -1); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)), QVariant()); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 3)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 4)), QVariant()); + + QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), idColumn); + QCOMPARE(model.headerData(1, Qt::Horizontal).toString(), QString("2")); + QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), nameColumn); + QCOMPARE(model.headerData(3, Qt::Horizontal).toString(), titleColumn); + QCOMPARE(model.headerData(4, Qt::Horizontal).toString(), QString("5")); + + QVERIFY(!model.insertColumn(-1)); + QVERIFY(!model.insertColumn(100)); + QVERIFY(!model.insertColumn(1, model.index(1, 1))); + + QVERIFY(model.insertColumn(0)); + + QCOMPARE(spy.count(), 2); + QVERIFY(*(QModelIndex *)spy.at(1).at(0).constData() == QModelIndex()); + QCOMPARE(spy.at(1).at(1).toInt(), 0); + QCOMPARE(spy.at(1).at(2).toInt(), 0); + + QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), 0); + QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), 1); + QCOMPARE(model.indexInQuery(model.index(0, 4)).column(), 2); + QCOMPARE(model.indexInQuery(model.index(0, 5)).column(), -1); + + QVERIFY(!model.insertColumn(6)); + QVERIFY(model.insertColumn(5)); + + QCOMPARE(spy.count(), 3); + QVERIFY(*(QModelIndex *)spy.at(2).at(0).constData() == QModelIndex()); + QCOMPARE(spy.at(2).at(1).toInt(), 5); + QCOMPARE(spy.at(2).at(2).toInt(), 5); + + QCOMPARE(model.indexInQuery(model.index(0, 0)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 1)).column(), 0); + QCOMPARE(model.indexInQuery(model.index(0, 2)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 3)).column(), 1); + QCOMPARE(model.indexInQuery(model.index(0, 4)).column(), 2); + QCOMPARE(model.indexInQuery(model.index(0, 5)).column(), -1); + QCOMPARE(model.indexInQuery(model.index(0, 6)).column(), -1); + + QCOMPARE(model.record().field(0).name(), QString()); + QCOMPARE(model.record().field(1).name(), idColumn); + QCOMPARE(model.record().field(2).name(), QString()); + QCOMPARE(model.record().field(3).name(), nameColumn); + QCOMPARE(model.record().field(4).name(), titleColumn); + QCOMPARE(model.record().field(5).name(), QString()); + QCOMPARE(model.record().field(6).name(), QString()); + + QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), QString("1")); + QCOMPARE(model.headerData(1, Qt::Horizontal).toString(), idColumn); + QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), QString("3")); + QCOMPARE(model.headerData(3, Qt::Horizontal).toString(), nameColumn); + QCOMPARE(model.headerData(4, Qt::Horizontal).toString(), titleColumn); + QCOMPARE(model.headerData(5, Qt::Horizontal).toString(), QString("6")); + QCOMPARE(model.headerData(6, Qt::Horizontal).toString(), QString("7")); +} + +void tst_QSqlQueryModel::record() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlQueryModel model; + model.setQuery(QSqlQuery("select * from " + qTableName("test", __FILE__), db)); + + QSqlRecord rec = model.record(); + + bool isToUpper = db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2"); + + QCOMPARE(rec.count(), 3); + QCOMPARE(rec.fieldName(0), isToUpper ? QString("ID") : QString("id")); + QCOMPARE(rec.fieldName(1), isToUpper ? QString("NAME") : QString("name")); + QCOMPARE(rec.fieldName(2), isToUpper ? QString("TITLE") : QString("title")); + QCOMPARE(rec.value(0), QVariant(rec.field(0).type())); + QCOMPARE(rec.value(1), QVariant(rec.field(1).type())); + QCOMPARE(rec.value(2), QVariant(rec.field(2).type())); + + rec = model.record(0); + QCOMPARE(rec.fieldName(0), isToUpper ? QString("ID") : QString("id")); + QCOMPARE(rec.fieldName(1), isToUpper ? QString("NAME") : QString("name")); + QCOMPARE(rec.fieldName(2), isToUpper ? QString("TITLE") : QString("title")); + QCOMPARE(rec.value(0).toString(), QString("1")); + QCOMPARE(rec.value(1), QVariant("harry")); + QCOMPARE(rec.value(2), QVariant(1)); +} + +void tst_QSqlQueryModel::setHeaderData() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlQueryModel model; + + QVERIFY(!model.setHeaderData(5, Qt::Vertical, "foo")); + QVERIFY(model.headerData(5, Qt::Vertical).isValid()); + + model.setQuery(QSqlQuery("select * from " + qTableName("test", __FILE__), db)); + + qRegisterMetaType<Qt::Orientation>("Qt::Orientation"); + QSignalSpy spy(&model, SIGNAL(headerDataChanged(Qt::Orientation, int, int))); + QVERIFY(model.setHeaderData(2, Qt::Horizontal, "bar")); + QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), QString("bar")); + QCOMPARE(spy.count(), 1); + QCOMPARE(qvariant_cast<Qt::Orientation>(spy.value(0).value(0)), Qt::Horizontal); + QCOMPARE(spy.value(0).value(1).toInt(), 2); + QCOMPARE(spy.value(0).value(2).toInt(), 2); + + QVERIFY(!model.setHeaderData(7, Qt::Horizontal, "foo", Qt::ToolTipRole)); + QVERIFY(!model.headerData(7, Qt::Horizontal, Qt::ToolTipRole).isValid()); + + bool isToUpper = db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2"); + QCOMPARE(model.headerData(0, Qt::Horizontal).toString(), isToUpper ? QString("ID") : QString("id")); + QCOMPARE(model.headerData(1, Qt::Horizontal).toString(), isToUpper ? QString("NAME") : QString("name")); + QCOMPARE(model.headerData(2, Qt::Horizontal).toString(), QString("bar")); + QVERIFY(model.headerData(3, Qt::Horizontal).isValid()); +} + +void tst_QSqlQueryModel::fetchMore() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlQueryModel model; + QSignalSpy spy(&model, SIGNAL(rowsInserted(QModelIndex, int, int))); + + model.setQuery(QSqlQuery("select * from " + qTableName("many", __FILE__), db)); + int rowCount = model.rowCount(); + + QCOMPARE(spy.value(0).value(1).toInt(), 0); + QCOMPARE(spy.value(0).value(2).toInt(), rowCount - 1); + + // If the driver doesn't return the query size fetchMore() causes the + // model to grow and new signals are emitted + if (!db.driver()->hasFeature(QSqlDriver::QuerySize)) { + spy.clear(); + model.fetchMore(); + int newRowCount = model.rowCount(); + QCOMPARE(spy.value(0).value(1).toInt(), rowCount); + QCOMPARE(spy.value(0).value(2).toInt(), newRowCount - 1); + } +} + +// For task 149491: When used with QSortFilterProxyModel, a view and a +// database that doesn't support the QuerySize feature, blank rows was +// appended if the query returned more than 256 rows and setQuery() +// was called more than once. This because an insertion of rows was +// triggered at the same time as the model was being cleared. +void tst_QSqlQueryModel::withSortFilterProxyModel() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + if (db.driver()->hasFeature(QSqlDriver::QuerySize)) + QSKIP("Test applies only for drivers not reporting the query size.", SkipSingle); + + QSqlQueryModel model; + model.setQuery(QSqlQuery("SELECT * FROM " + qTableName("test3", __FILE__), db)); + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + + QTableView view; + view.setModel(&proxy); + + QSignalSpy modelRowsRemovedSpy(&model, SIGNAL(rowsRemoved(const QModelIndex &, int, int))); + QSignalSpy modelRowsInsertedSpy(&model, SIGNAL(rowsInserted(const QModelIndex &, int, int))); + model.setQuery(QSqlQuery("SELECT * FROM " + qTableName("test3", __FILE__), db)); + view.scrollToBottom(); + + QTestEventLoop::instance().enterLoop(1); + + QCOMPARE(proxy.rowCount(), 511); + + // The second call to setQuery() clears the model by removing it's rows. + // Only 256 rows because that is what was cached. + QCOMPARE(modelRowsRemovedSpy.count(), 1); + QCOMPARE(modelRowsRemovedSpy.value(0).value(1).toInt(), 0); + QCOMPARE(modelRowsRemovedSpy.value(0).value(2).toInt(), 255); + + // The call to scrollToBottom() forces the model to fetch all rows, + // which will be done in two steps. + QCOMPARE(modelRowsInsertedSpy.count(), 2); + QCOMPARE(modelRowsInsertedSpy.value(0).value(1).toInt(), 0); + QCOMPARE(modelRowsInsertedSpy.value(0).value(2).toInt(), 255); + QCOMPARE(modelRowsInsertedSpy.value(1).value(1).toInt(), 256); + QCOMPARE(modelRowsInsertedSpy.value(1).value(2).toInt(), 510); +} + +// For task 155402: When the model is already empty when setQuery() is called +// no rows have to be removed and rowsAboutToBeRemoved and rowsRemoved should +// not be emitted. +void tst_QSqlQueryModel::setQuerySignalEmission() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlQueryModel model; + QSignalSpy modelRowsAboutToBeRemovedSpy(&model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int))); + QSignalSpy modelRowsRemovedSpy(&model, SIGNAL(rowsRemoved(const QModelIndex &, int, int))); + + // First select, the model was empty and no rows had to be removed! + model.setQuery(QSqlQuery("SELECT * FROM " + qTableName("test", __FILE__), db)); + QCOMPARE(modelRowsAboutToBeRemovedSpy.count(), 0); + QCOMPARE(modelRowsRemovedSpy.count(), 0); + + // Second select, the model wasn't empty and two rows had to be removed! + model.setQuery(QSqlQuery("SELECT * FROM " + qTableName("test", __FILE__), db)); + QCOMPARE(modelRowsAboutToBeRemovedSpy.count(), 1); + QCOMPARE(modelRowsAboutToBeRemovedSpy.value(0).value(1).toInt(), 0); + QCOMPARE(modelRowsAboutToBeRemovedSpy.value(0).value(2).toInt(), 1); + QCOMPARE(modelRowsRemovedSpy.count(), 1); + QCOMPARE(modelRowsRemovedSpy.value(0).value(1).toInt(), 0); + QCOMPARE(modelRowsRemovedSpy.value(0).value(2).toInt(), 1); +} + +// For task 170783: When the query's result set is empty no rows should be inserted, +// i.e. no rowsAboutToBeInserted or rowsInserted signals should be emitted. +void tst_QSqlQueryModel::setQueryWithNoRowsInResultSet() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlQueryModel model; + QSignalSpy modelRowsAboutToBeInsertedSpy(&model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int))); + QSignalSpy modelRowsInsertedSpy(&model, SIGNAL(rowsInserted(const QModelIndex &, int, int))); + + // The query's result set will be empty so no signals should be emitted! + QSqlQuery query(db); + QVERIFY_SQL(query, exec("SELECT * FROM " + qTableName("test", __FILE__) + " where 0 = 1")); + model.setQuery(query); + QCOMPARE(modelRowsAboutToBeInsertedSpy.count(), 0); + QCOMPARE(modelRowsInsertedSpy.count(), 0); +} + +// For task 180617 +// According to the task, several specific duplicate SQL queries would cause +// multiple empty grid lines to be visible in the view +void tst_QSqlQueryModel::task_180617() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + const QString test3(qTableName("test3", __FILE__)); + + QTableView view; + QCOMPARE(view.columnAt(0), -1); + QCOMPARE(view.rowAt(0), -1); + + QSqlQueryModel model; + model.setQuery( "SELECT TOP 0 * FROM " + test3, db ); + view.setModel(&model); + + bool error = false; + // Usually a syntax error + if (model.lastError().isValid()) // usually a syntax error + error = true; + + QCOMPARE(view.columnAt(0), (error)?-1:0 ); + QCOMPARE(view.rowAt(0), -1); + + model.setQuery( "SELECT TOP 0 * FROM " + test3, db ); + model.setQuery( "SELECT TOP 0 * FROM " + test3, db ); + model.setQuery( "SELECT TOP 0 * FROM " + test3, db ); + model.setQuery( "SELECT TOP 0 * FROM " + test3, db ); + + QCOMPARE(view.columnAt(0), (error)?-1:0 ); + QCOMPARE(view.rowAt(0), -1); +} + +void tst_QSqlQueryModel::task_QTBUG_4963_setHeaderDataWithProxyModel() +{ + QSqlQueryModel plainModel; + QSortFilterProxyModel proxyModel; + proxyModel.setSourceModel(&plainModel); + QVERIFY(!plainModel.setHeaderData(0, Qt::Horizontal, QObject::tr("ID"))); + // And it should not crash. +} + +QTEST_MAIN(tst_QSqlQueryModel) +#include "tst_qsqlquerymodel.moc" diff --git a/tests/auto/sql/models/qsqlrelationaltablemodel/.gitignore b/tests/auto/sql/models/qsqlrelationaltablemodel/.gitignore new file mode 100644 index 0000000000..b6f469c4dc --- /dev/null +++ b/tests/auto/sql/models/qsqlrelationaltablemodel/.gitignore @@ -0,0 +1 @@ +tst_qsqlrelationaltablemodel diff --git a/tests/auto/sql/models/qsqlrelationaltablemodel/qsqlrelationaltablemodel.pro b/tests/auto/sql/models/qsqlrelationaltablemodel/qsqlrelationaltablemodel.pro new file mode 100644 index 0000000000..dad42d512c --- /dev/null +++ b/tests/auto/sql/models/qsqlrelationaltablemodel/qsqlrelationaltablemodel.pro @@ -0,0 +1,25 @@ +load(qttest_p4) +SOURCES += tst_qsqlrelationaltablemodel.cpp + +QT += sql + +wince*: { + plugFiles.files = ../../../plugins/sqldrivers + plugFiles.path = . + DEPLOYMENT += plugFiles + LIBS += -lws2 +}else:symbian { + qt_not_deployed { + contains(S60_VERSION, 3.1)|contains(S60_VERSION, 3.2)|contains(S60_VERSION, 5.0) { + sqlite.path = /sys/bin + sqlite.files = sqlite3.dll + DEPLOYMENT += sqlite + } + } +} else { + win32-g++* { + LIBS += -lws2_32 + } else:win32 { + LIBS += ws2_32.lib + } +} diff --git a/tests/auto/sql/models/qsqlrelationaltablemodel/tst_qsqlrelationaltablemodel.cpp b/tests/auto/sql/models/qsqlrelationaltablemodel/tst_qsqlrelationaltablemodel.cpp new file mode 100644 index 0000000000..2c5af13c0c --- /dev/null +++ b/tests/auto/sql/models/qsqlrelationaltablemodel/tst_qsqlrelationaltablemodel.cpp @@ -0,0 +1,1494 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtSql/QtSql> + +#include "../../kernel/qsqldatabase/tst_databases.h" + +const QString reltest1(qTableName("reltest1", __FILE__)), + reltest2(qTableName("reltest2", __FILE__)), + reltest3(qTableName("reltest3", __FILE__)), + reltest4(qTableName("reltest4", __FILE__)), + reltest5(qTableName("reltest5", __FILE__)); + + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QSqlRelationalTableModel : public QObject +{ + Q_OBJECT + +public: + void recreateTestTables(QSqlDatabase); + + tst_Databases dbs; + +public slots: + void initTestCase_data(); + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void data(); + void setData(); + void multipleRelation(); + void insertRecord(); + void setRecord(); + void insertWithStrategies(); + void removeColumn(); + void filter(); + void sort(); + void revert(); + + void clearDisplayValuesCache(); + void insertRecordDuplicateFieldNames(); + void invalidData(); + void relationModel(); + void casing(); + void escapedRelations(); + void escapedTableName(); + void whiteSpaceInIdentifiers(); + void psqlSchemaTest(); + void selectAfterUpdate(); + +private: + void dropTestTables( QSqlDatabase db ); +}; + + +void tst_QSqlRelationalTableModel::initTestCase_data() +{ + dbs.open(); + if (dbs.fillTestTable() == 0) { + qWarning("NO DATABASES"); + QSKIP("No database drivers are available in this Qt configuration", SkipAll); + } +} + +void tst_QSqlRelationalTableModel::recreateTestTables(QSqlDatabase db) +{ + dropTestTables(db); + + QSqlQuery q(db); + QVERIFY_SQL( q, exec("create table " + reltest1 + + " (id int not null primary key, name varchar(20), title_key int, another_title_key int)")); + QVERIFY_SQL( q, exec("insert into " + reltest1 + " values(1, 'harry', 1, 2)")); + QVERIFY_SQL( q, exec("insert into " + reltest1 + " values(2, 'trond', 2, 1)")); + QVERIFY_SQL( q, exec("insert into " + reltest1 + " values(3, 'vohi', 1, 2)")); + QVERIFY_SQL( q, exec("insert into " + reltest1 + " values(4, 'boris', 2, 2)")); + QVERIFY_SQL( q, exec("insert into " + reltest1 + " values(5, 'nat', NULL, NULL)")); + QVERIFY_SQL( q, exec("insert into " + reltest1 + " values(6, 'ale', NULL, 2)")); + + QVERIFY_SQL( q, exec("create table " + reltest2 + " (tid int not null primary key, title varchar(20))")); + QVERIFY_SQL( q, exec("insert into " + reltest2 + " values(1, 'herr')")); + QVERIFY_SQL( q, exec("insert into " + reltest2 + " values(2, 'mister')")); + + QVERIFY_SQL( q, exec("create table " + reltest3 + " (id int not null primary key, name varchar(20), city_key int)")); + QVERIFY_SQL( q, exec("insert into " + reltest3 + " values(1, 'Gustav', 1)")); + QVERIFY_SQL( q, exec("insert into " + reltest3 + " values(2, 'Heidi', 2)")); + + QVERIFY_SQL( q, exec("create table " + reltest4 + " (id int not null primary key, name varchar(20))")); + QVERIFY_SQL( q, exec("insert into " + reltest4 + " values(1, 'Oslo')")); + QVERIFY_SQL( q, exec("insert into " + reltest4 + " values(2, 'Trondheim')")); + + QVERIFY_SQL( q, exec("create table " + reltest5 + " (title varchar(20) not null primary key, abbrev varchar(20))")); + QVERIFY_SQL( q, exec("insert into " + reltest5 + " values('herr', 'Hr')")); + QVERIFY_SQL( q, exec("insert into " + reltest5 + " values('mister', 'Mr')")); + + if (testWhiteSpaceNames(db.driverName())) { + QString reltest6 = db.driver()->escapeIdentifier(qTableName("rel", __FILE__)+" test6", QSqlDriver::TableName); + QVERIFY_SQL( q, exec("create table " + reltest6 + " (id int not null primary key, " + db.driver()->escapeIdentifier("city key", QSqlDriver::FieldName) + + " int, " + db.driver()->escapeIdentifier("extra field", QSqlDriver::FieldName) + " int)")); + QVERIFY_SQL( q, exec("insert into " + reltest6 + " values(1, 1,9)")); + QVERIFY_SQL( q, exec("insert into " + reltest6 + " values(2, 2,8)")); + + QString reltest7 = db.driver()->escapeIdentifier(qTableName("rel", __FILE__)+" test7", QSqlDriver::TableName); + QVERIFY_SQL( q, exec("create table " + reltest7 + " (" + db.driver()->escapeIdentifier("city id", QSqlDriver::TableName) + " int not null primary key, " + db.driver()->escapeIdentifier("city name", QSqlDriver::FieldName) + " varchar(20))")); + QVERIFY_SQL( q, exec("insert into " + reltest7 + " values(1, 'New York')")); + QVERIFY_SQL( q, exec("insert into " + reltest7 + " values(2, 'Washington')")); + } +} + +void tst_QSqlRelationalTableModel::initTestCase() +{ + foreach (const QString &dbname, dbs.dbNames) { + QSqlDatabase db=QSqlDatabase::database(dbname); + if (db.driverName().startsWith("QIBASE")) + db.exec("SET DIALECT 3"); + else if (tst_Databases::isSqlServer(db)) { + db.exec("SET ANSI_DEFAULTS ON"); + db.exec("SET IMPLICIT_TRANSACTIONS OFF"); + } + else if(tst_Databases::isPostgreSQL(db)) + db.exec("set client_min_messages='warning'"); + recreateTestTables(db); + } +} + +void tst_QSqlRelationalTableModel::cleanupTestCase() +{ + foreach (const QString &dbName, dbs.dbNames) { + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE( db ); + dropTestTables( QSqlDatabase::database(dbName) ); + } + dbs.close(); +} + +void tst_QSqlRelationalTableModel::dropTestTables( QSqlDatabase db ) +{ + QStringList tableNames; + tableNames << reltest1 + << reltest2 + << reltest3 + << reltest4 + << reltest5 + << (qTableName( "rel", __FILE__)+" test6") + << (qTableName( "rel", __FILE__)+" test7") + << qTableName("CASETEST1", db.driver() ) + << qTableName("casetest1", db.driver() ); + tst_Databases::safeDropTables( db, tableNames ); + + db.exec("DROP SCHEMA "+qTableName("QTBUG_5373", __FILE__)+" CASCADE"); + db.exec("DROP SCHEMA "+qTableName("QTBUG_5373_s2", __FILE__)+" CASCADE"); +} + +void tst_QSqlRelationalTableModel::init() +{ +} + +void tst_QSqlRelationalTableModel::cleanup() +{ +} + +void tst_QSqlRelationalTableModel::data() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.columnCount(), 4); + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + + //try a non-existent index + QVERIFY2(model.data(model.index(0,4)).isValid() == false,"Invalid index returned valid QVariant"); + + // check row with null relation: they are reported only in LeftJoin mode + QCOMPARE(model.rowCount(), 4); + + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(4, 0)).toInt(), 5); + QCOMPARE(model.data(model.index(4, 1)).toString(), QString("nat")); + QVERIFY2(model.data(model.index(4, 2)).isValid() == true, "NULL relation reported with invalid QVariant"); + + //check data retrieval when relational key is a non-integer type + //in this case a string + QSqlRelationalTableModel model2(0,db); + model2.setTable(reltest2); + model2.setRelation(1, QSqlRelation(reltest5,"title","abbrev")); + QVERIFY_SQL(model2, select()); + + QCOMPARE(model2.data(model2.index(0, 1)).toString(), QString("Hr")); + QCOMPARE(model2.data(model2.index(1, 1)).toString(), QString("Mr")); +} + +void tst_QSqlRelationalTableModel::setData() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + // set the values using OnRowChange Strategy + { + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest1); + model.setSort(0, Qt::AscendingOrder); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + + QVERIFY(model.setData(model.index(0, 1), QString("harry2"))); + QVERIFY(model.setData(model.index(0, 2), 2)); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry2")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("mister")); + + model.submit(); + + QVERIFY(model.setData(model.index(3,1), QString("boris2"))); + QVERIFY(model.setData(model.index(3, 2), 1)); + + QCOMPARE(model.data(model.index(3,1)).toString(), QString("boris2")); + QCOMPARE(model.data(model.index(3, 2)).toString(), QString("herr")); + + model.submit(); + } + { //verify values + QSqlRelationalTableModel model(0, db); + model.setTable(reltest1); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry2")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 2); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("boris2")); + QCOMPARE(model.data(model.index(3, 2)).toInt(), 1); + + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("mister")); + QCOMPARE(model.data(model.index(3,2)).toString(), QString("herr")); + + } + + //set the values using OnFieldChange strategy + { + QSqlRelationalTableModel model(0, db); + model.setTable(reltest1); + model.setEditStrategy(QSqlTableModel::OnFieldChange); + model.setSort(0, Qt::AscendingOrder); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + + QVERIFY(model.setData(model.index(1,1), QString("trond2"))); + QVERIFY(model.setData(model.index(2,2), 2)); + + QCOMPARE(model.data(model.index(1,1)).toString(), QString("trond2")); + QCOMPARE(model.data(model.index(2,2)).toString(), QString("mister")); + } + { //verify values + QSqlRelationalTableModel model(0, db); + model.setTable(reltest1); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond2")); + QCOMPARE(model.data(model.index(2, 2)).toInt(), 2); + + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + QCOMPARE(model.data(model.index(2, 2)).toString(), QString("mister")); + } + + //set values using OnManualSubmit strategy + { + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + + //sybase doesn't allow tables with the same alias used twice as col names + //so don't set up an identical relation when using the tds driver + if (!db.driverName().startsWith("QTDS")) + model.setRelation(3, QSqlRelation(reltest2, "tid", "title")); + + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QVERIFY(model.setData(model.index(2, 1), QString("vohi2"))); + QVERIFY(model.setData(model.index(3, 2), 1)); + QVERIFY(model.setData(model.index(0, 3), 1)); + + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi2")); + QCOMPARE(model.data(model.index(3, 2)).toString(), QString("herr")); + if (!db.driverName().startsWith("QTDS")) + QCOMPARE(model.data(model.index(0, 3)).toString(), QString("herr")); + else + QCOMPARE(model.data(model.index(0, 3)).toInt(), 1); + + QVERIFY_SQL(model, submitAll()); + } + { //verify values + QSqlRelationalTableModel model(0, db); + model.setTable(reltest1); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi2")); + QCOMPARE(model.data(model.index(3, 2)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 3)).toInt(), 1); + + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + if (!db.driverName().startsWith("QTDS")) + model.setRelation(3, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + QCOMPARE(model.data(model.index(3, 2)).toString(), QString("herr")); + + if (!db.driverName().startsWith("QTDS")) + QCOMPARE(model.data(model.index(0, 3)).toString(), QString("herr")); + else + QCOMPARE(model.data(model.index(0, 3)).toInt(), 1); + } + + //check setting of data when the relational key is a non-integer type + //in this case a string. + { + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest2); + model.setRelation(1, QSqlRelation(reltest5, "title", "abbrev")); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0,1)).toString(), QString("Hr")); + QVERIFY(model.setData(model.index(0,1), QString("mister"))); + QCOMPARE(model.data(model.index(0,1)).toString(), QString("Mr")); + QVERIFY_SQL(model, submitAll()); + + QCOMPARE(model.data(model.index(0,1)).toString(), QString("Mr")); + } + + // Redo same tests, with a LeftJoin + { + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest2); + model.setRelation(1, QSqlRelation(reltest5, "title", "abbrev")); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0,1)).toString(), QString("Mr")); + QVERIFY(model.setData(model.index(0,1), QString("herr"))); + QCOMPARE(model.data(model.index(0,1)).toString(), QString("Hr")); + QVERIFY_SQL(model, submitAll()); + + QCOMPARE(model.data(model.index(0,1)).toString(), QString("Hr")); + } + +} + +void tst_QSqlRelationalTableModel::multipleRelation() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + recreateTestTables(db); + + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + model.setRelation(3, QSqlRelation(reltest4, "id", "name")); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(2, 0)).toInt(), 3); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + QCOMPARE(model.data(model.index(0, 3)).toString(), QString("Trondheim")); + + // Redo same test in the LeftJoin mode + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + model.setRelation(3, QSqlRelation(reltest4, "id", "name")); + model.setSort(0, Qt::AscendingOrder); + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(2, 0)).toInt(), 3); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + QCOMPARE(model.data(model.index(0, 3)).toString(), QString("Trondheim")); +} + +void tst_QSqlRelationalTableModel::insertRecord() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QSqlRecord rec; + QSqlField f1("id", QVariant::Int); + QSqlField f2("name", QVariant::String); + QSqlField f3("title_key", QVariant::Int); + QSqlField f4("another_title_key", QVariant::Int); + + f1.setValue(7); + f2.setValue("test"); + f3.setValue(1); + f4.setValue(2); + + f1.setGenerated(true); + f2.setGenerated(true); + f3.setGenerated(true); + f4.setGenerated(true); + + rec.append(f1); + rec.append(f2); + rec.append(f3); + rec.append(f4); + + QVERIFY_SQL(model, insertRecord(-1, rec)); + + QCOMPARE(model.data(model.index(4, 0)).toInt(), 7); + QCOMPARE(model.data(model.index(4, 1)).toString(), QString("test")); + QCOMPARE(model.data(model.index(4, 2)).toString(), QString("herr")); + + // In LeftJoin mode, two additional rows are fetched + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(6, 0)).toInt(), 7); + QCOMPARE(model.data(model.index(6, 1)).toString(), QString("test")); + QCOMPARE(model.data(model.index(6, 2)).toString(), QString("herr")); +} + +void tst_QSqlRelationalTableModel::setRecord() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + recreateTestTables(db); + + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QSqlRecord rec; + QSqlField f1("id", QVariant::Int); + QSqlField f2("name", QVariant::String); + QSqlField f3("title_key", QVariant::Int); + QSqlField f4("another_title_key", QVariant::Int); + + f1.setValue(7); + f2.setValue("tester"); + f3.setValue(1); + f4.setValue(2); + + f1.setGenerated(true); + f2.setGenerated(true); + f3.setGenerated(true); + f4.setGenerated(true); + + rec.append(f1); + rec.append(f2); + rec.append(f3); + rec.append(f4); + + QCOMPARE(model.data(model.index(1, 0)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(1, 2)).toString(), QString("mister")); + + QVERIFY_SQL(model, setRecord(1, rec)); + + QCOMPARE(model.data(model.index(1, 0)).toInt(), 7); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("tester")); + QCOMPARE(model.data(model.index(1, 2)).toString(), QString("herr")); + + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, submit()); + + QCOMPARE(model.data(model.index(3, 0)).toInt(), 7); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("tester")); + QCOMPARE(model.data(model.index(3, 2)).toString(), QString("herr")); + +} + +void tst_QSqlRelationalTableModel::insertWithStrategies() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + model.setSort(0, Qt::AscendingOrder); + + if (!db.driverName().startsWith("QTDS")) + model.setRelation(3, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0,0)).toInt(), 1); + QCOMPARE(model.data(model.index(0,1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0,2)).toString(), QString("herr")); + if (!db.driverName().startsWith("QTDS")) + QCOMPARE(model.data(model.index(0,3)).toString(), QString("mister")); + else + QCOMPARE(model.data(model.index(0,3)).toInt(), 2); + + model.insertRows(0, 1); + model.setData(model.index(0, 0), 1011); + model.setData(model.index(0, 1), "test"); + model.setData(model.index(0, 2), 2); + model.setData(model.index(0, 3), 1); + + QCOMPARE(model.data(model.index(0,0)).toInt(), 1011); + QCOMPARE(model.data(model.index(0,1)).toString(), QString("test")); + QCOMPARE(model.data(model.index(0,2)).toString(), QString("mister")); + if (!db.driverName().startsWith("QTDS")) + QCOMPARE(model.data(model.index(0,3)).toString(), QString("herr")); + else + QCOMPARE(model.data(model.index(0,3)).toInt(), 1); + + QCOMPARE(model.data(model.index(1,0)).toInt(), 1); + QCOMPARE(model.data(model.index(1,1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1,2)).toString(), QString("herr")); + if (!db.driverName().startsWith("QTDS")) + QCOMPARE(model.data(model.index(1,3)).toString(), QString("mister")); + else + QCOMPARE(model.data(model.index(1,3)).toInt(), 2); + + + + QVERIFY_SQL(model, submitAll()); + + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + + QCOMPARE(model.data(model.index(0,0)).toInt(), 1); + QCOMPARE(model.data(model.index(0,1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0,2)).toString(), QString("herr")); + + if (!db.driverName().startsWith("QTDS")) { + QCOMPARE(model.data(model.index(0,3)).toString(), QString("mister")); + model.setData(model.index(0,3),1); + QCOMPARE(model.data(model.index(0,3)).toString(), QString("herr")); + } else { + QCOMPARE(model.data(model.index(0,3)).toInt(), 2); + model.setData(model.index(0,3),1); + QCOMPARE(model.data(model.index(0,3)).toInt(), 1); + } + + model.insertRows(0, 2); + model.setData(model.index(0, 0), 1012); + model.setData(model.index(0, 1), "george"); + model.setData(model.index(0, 2), 2); + model.setData(model.index(0, 3), 2); + + model.setData(model.index(1, 0), 1013); + model.setData(model.index(1, 1), "kramer"); + model.setData(model.index(1, 2), 2); + model.setData(model.index(1, 3), 1); + + QCOMPARE(model.data(model.index(0,0)).toInt(),1012); + QCOMPARE(model.data(model.index(0,1)).toString(), QString("george")); + QCOMPARE(model.data(model.index(0,2)).toString(), QString("mister")); + if (!db.driverName().startsWith("QTDS")) + QCOMPARE(model.data(model.index(0,3)).toString(), QString("mister")); + else + QCOMPARE(model.data(model.index(0,3)).toInt(), 2); + + + QCOMPARE(model.data(model.index(1,0)).toInt(),1013); + QCOMPARE(model.data(model.index(1,1)).toString(), QString("kramer")); + QCOMPARE(model.data(model.index(1,2)).toString(), QString("mister")); + if (!db.driverName().startsWith("QTDS")) + QCOMPARE(model.data(model.index(1,3)).toString(), QString("herr")); + else + QCOMPARE(model.data(model.index(1,3)).toInt(), 1); + + QCOMPARE(model.data(model.index(2,0)).toInt(), 1); + QCOMPARE(model.data(model.index(2,1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(2,2)).toString(), QString("herr")); + if (!db.driverName().startsWith("QTDS")) + QCOMPARE(model.data(model.index(2,3)).toString(), QString("herr")); + else + QCOMPARE(model.data(model.index(2,3)).toInt(), 1); + + QVERIFY_SQL(model, submitAll()); +} + +void tst_QSqlRelationalTableModel::removeColumn() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + recreateTestTables(db); + + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + + QVERIFY_SQL(model, removeColumn(3)); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.columnCount(), 3); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + QCOMPARE(model.data(model.index(0, 3)), QVariant()); + + // try removing more than one column + QVERIFY_SQL(model, removeColumns(1, 2)); + QCOMPARE(model.columnCount(), 1); + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)), QVariant()); + + // try in LeftJoin mode the same tests + CHECK_DATABASE(db); + recreateTestTables(db); + + QSqlRelationalTableModel lmodel(0, db); + + lmodel.setTable(reltest1); + lmodel.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + lmodel.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(lmodel, select()); + + QVERIFY_SQL(lmodel, removeColumn(3)); + QVERIFY_SQL(lmodel, select()); + + QCOMPARE(lmodel.columnCount(), 3); + + QCOMPARE(lmodel.data(lmodel.index(0, 0)).toInt(), 1); + QCOMPARE(lmodel.data(lmodel.index(0, 1)).toString(), QString("harry")); + QCOMPARE(lmodel.data(lmodel.index(0, 2)).toString(), QString("herr")); + QCOMPARE(lmodel.data(lmodel.index(0, 3)), QVariant()); + + // try removing more than one column + QVERIFY_SQL(lmodel, removeColumns(1, 2)); + QCOMPARE(lmodel.columnCount(), 1); + QCOMPARE(lmodel.data(lmodel.index(0, 0)).toInt(), 1); + QCOMPARE(lmodel.data(lmodel.index(0, 1)), QVariant()); +} + +void tst_QSqlRelationalTableModel::filter() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + recreateTestTables(db); + + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + model.setFilter("title = 'herr'"); + + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 2); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + QCOMPARE(model.data(model.index(1, 2)).toString(), QString("herr")); + + // Redo same filter test in LeftJoin mode + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model,select()); + + QCOMPARE(model.rowCount(), 2); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + QCOMPARE(model.data(model.index(1, 2)).toString(), QString("herr")); +} + +void tst_QSqlRelationalTableModel::sort() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + if (!db.driverName().startsWith("QTDS")) + model.setRelation(3, QSqlRelation(reltest2, "tid", "title")); + + model.setSort(2, Qt::DescendingOrder); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.rowCount(), 4); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("mister")); + QCOMPARE(model.data(model.index(1, 2)).toString(), QString("mister")); + QCOMPARE(model.data(model.index(2, 2)).toString(), QString("herr")); + QCOMPARE(model.data(model.index(3, 2)).toString(), QString("herr")); + + + model.setSort(3, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + if (!db.driverName().startsWith("QTDS")) { + QCOMPARE(model.rowCount(), 4); + QCOMPARE(model.data(model.index(0, 3)).toString(), QString("herr")); + QCOMPARE(model.data(model.index(1, 3)).toString(), QString("mister")); + QCOMPARE(model.data(model.index(2, 3)).toString(), QString("mister")); + QCOMPARE(model.data(model.index(3, 3)).toString(), QString("mister")); + } else { + QCOMPARE(model.data(model.index(0, 3)).toInt(), 1); + QCOMPARE(model.data(model.index(1, 3)).toInt(), 2); + QCOMPARE(model.data(model.index(2, 3)).toInt(), 2); + QCOMPARE(model.data(model.index(3, 3)).toInt(), 2); + } + + // redo same test in LeftJoin mode + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + model.setSort(2, Qt::DescendingOrder); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.rowCount(), 6); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("mister")); + QCOMPARE(model.data(model.index(1, 2)).toString(), QString("mister")); + QCOMPARE(model.data(model.index(2, 2)).toString(), QString("herr")); + QCOMPARE(model.data(model.index(3, 2)).toString(), QString("herr")); + QCOMPARE(model.data(model.index(4, 2)).toString(), QString("")); + QCOMPARE(model.data(model.index(5, 2)).toString(), QString("")); + + model.setSort(3, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + if (!db.driverName().startsWith("QTDS")) { + QCOMPARE(model.rowCount(), 6); + QCOMPARE(model.data(model.index(0, 3)).toString(), QString("")); + QCOMPARE(model.data(model.index(1, 3)).toString(), QString("herr")); + QCOMPARE(model.data(model.index(2, 3)).toString(), QString("mister")); + QCOMPARE(model.data(model.index(3, 3)).toString(), QString("mister")); + QCOMPARE(model.data(model.index(4, 3)).toString(), QString("mister")); + QCOMPARE(model.data(model.index(5, 3)).toString(), QString("mister")); + } else { + QCOMPARE(model.data(model.index(0, 3)).toInt(), 1); + QCOMPARE(model.data(model.index(1, 3)).toInt(), 2); + QCOMPARE(model.data(model.index(2, 3)).toInt(), 2); + QCOMPARE(model.data(model.index(3, 3)).toInt(), 2); + } +} + +static void testRevert(QSqlRelationalTableModel &model) +{ + /* revert single row */ + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + QVERIFY(model.setData(model.index(0, 2), 2, Qt::EditRole)); + + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("mister")); + model.revertRow(0); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + + /* revert all */ + QVERIFY(model.setData(model.index(0, 2), 2, Qt::EditRole)); + + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("mister")); + model.revertAll(); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + + // the following only works for OnManualSubmit + if (model.editStrategy() != QSqlTableModel::OnManualSubmit) + return; + + /* revert inserted rows */ + int initialRowCount = model.rowCount(); + QVERIFY(model.insertRows(4, 4)); + QVERIFY(model.rowCount() == (initialRowCount + 4)); + + /* make sure the new rows are initialized to nothing */ + QVERIFY(model.data(model.index(4, 2)).toString().isEmpty()); + QVERIFY(model.data(model.index(5, 2)).toString().isEmpty()); + QVERIFY(model.data(model.index(6, 2)).toString().isEmpty()); + QVERIFY(model.data(model.index(7, 2)).toString().isEmpty()); + + /* Set some values */ + QVERIFY(model.setData(model.index(4, 0), 42, Qt::EditRole)); + QVERIFY(model.setData(model.index(5, 0), 43, Qt::EditRole)); + QVERIFY(model.setData(model.index(6, 0), 44, Qt::EditRole)); + QVERIFY(model.setData(model.index(7, 0), 45, Qt::EditRole)); + + QVERIFY(model.setData(model.index(4, 2), 2, Qt::EditRole)); + QVERIFY(model.setData(model.index(5, 2), 2, Qt::EditRole)); + QVERIFY(model.setData(model.index(6, 2), 1, Qt::EditRole)); + QVERIFY(model.setData(model.index(7, 2), 2, Qt::EditRole)); + + /* Now revert the newly inserted rows */ + model.revertAll(); + QVERIFY(model.rowCount() == initialRowCount); + + /* Insert rows again */ + QVERIFY(model.insertRows(4, 4)); + + /* make sure the new rows are initialized to nothing */ + QVERIFY(model.data(model.index(4, 2)).toString().isEmpty()); + QVERIFY(model.data(model.index(5, 2)).toString().isEmpty()); + QVERIFY(model.data(model.index(6, 2)).toString().isEmpty()); + QVERIFY(model.data(model.index(7, 2)).toString().isEmpty()); +} + +void tst_QSqlRelationalTableModel::revert() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + model.setRelation(3, QSqlRelation(reltest4, "id", "name")); + + model.setSort(0, Qt::AscendingOrder); + + QVERIFY_SQL(model, select()); + QCOMPARE(model.data(model.index(0, 0)).toString(), QString("1")); + + testRevert(model); + if (QTest::currentTestFailed()) + return; + + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + testRevert(model); + + /* and again with OnManualSubmit */ + model.setJoinMode(QSqlRelationalTableModel::InnerJoin); + QVERIFY_SQL(model, select()); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + testRevert(model); + + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + testRevert(model); +} + +void tst_QSqlRelationalTableModel::clearDisplayValuesCache() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlRelationalTableModel model(0, db); + + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + + if (!db.driverName().startsWith("QTDS")) + model.setRelation(3, QSqlRelation(reltest2, "tid", "title")); + model.setSort(1, Qt::AscendingOrder); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(3, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(3, 2)).toString(), QString("herr")); + if (!db.driverName().startsWith("QTDS")) + QCOMPARE(model.data(model.index(3, 3)).toString(), QString("mister")); + else + QCOMPARE(model.data(model.index(3, 3)).toInt(), 2 ); + + model.insertRow(model.rowCount()); + QVERIFY(model.setData(model.index(4, 0), 7, Qt::EditRole)); + QVERIFY(model.setData(model.index(4, 1), "anders", Qt::EditRole)); + QVERIFY(model.setData(model.index(4, 2), 1, Qt::EditRole)); + QVERIFY(model.setData(model.index(4, 3), 1, Qt::EditRole)); + model.submitAll(); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 7); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("anders")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + if (!db.driverName().startsWith("QTDS")) + QCOMPARE(model.data(model.index(0, 3)).toString(), QString("herr")); + else + QCOMPARE(model.data(model.index(0, 3)).toInt(), 1); + + QCOMPARE(model.data(model.index(4, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(4, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(4, 2)).toString(), QString("herr")); + if (!db.driverName().startsWith("QTDS")) + QCOMPARE(model.data(model.index(4, 3)).toString(), QString("mister")); + else + QCOMPARE(model.data(model.index(4, 3)).toInt(), 2); +} + +// For task 140782 and 176374: If the main table and the the related tables uses the same +// name for a column or display column then insertRecord() would return true though it +// actually failed. +void tst_QSqlRelationalTableModel::insertRecordDuplicateFieldNames() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlRelationalTableModel model(0, db); + model.setTable(reltest3); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + model.setSort(0, Qt::AscendingOrder); + + // Duplication of "name", used in both reltest3 and reltest4. + model.setRelation(2, QSqlRelation(reltest4, "id", "name")); + QVERIFY_SQL(model, select()); + + if (db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2")) { + QCOMPARE(model.record(1).value((reltest4+QLatin1String("_name_2")).toUpper()).toString(), + QString("Trondheim")); + } else { + QCOMPARE(model.record(1).value((reltest4+QLatin1String("_name_2"))).toString(), + QString("Trondheim")); + } + + QSqlRecord rec = model.record(); + rec.setValue(0, 3); + rec.setValue(1, "Berge"); + rec.setValue(2, 1); // Must insert the key value + + if (db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2")) { + QCOMPARE(rec.fieldName(0), QLatin1String("ID")); + QCOMPARE(rec.fieldName(1), QLatin1String("NAME")); // This comes from main table + } else { + QCOMPARE(rec.fieldName(0), QLatin1String("id")); + QCOMPARE(rec.fieldName(1), QLatin1String("name")); + } + + // The duplicate field names is aliased because it's comes from the relation's display column. + if(db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2")) + QCOMPARE(rec.fieldName(2), (reltest4+QLatin1String("_name_2")).toUpper()); + else + QCOMPARE(rec.fieldName(2), reltest4+QLatin1String("_name_2")); + + QVERIFY(model.insertRecord(-1, rec)); + QCOMPARE(model.data(model.index(2, 2)).toString(), QString("Oslo")); + QVERIFY(model.submitAll()); + QCOMPARE(model.data(model.index(2, 2)).toString(), QString("Oslo")); +} + +void tst_QSqlRelationalTableModel::invalidData() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlRelationalTableModel model(0, db); + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + + //try set a non-existent relational key + QVERIFY(model.setData(model.index(0, 2), 3) == false); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + + //try to set data in non valid index + QVERIFY(model.setData(model.index(0,10),5) == false); + + //same test with LeftJoin mode + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + //try set a non-existent relational key + QVERIFY(model.setData(model.index(0, 2), 3) == false); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + + //try to set data in non valid index + QVERIFY(model.setData(model.index(0,10),5) == false); +} + +void tst_QSqlRelationalTableModel::relationModel() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlRelationalTableModel model(0, db); + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + + QVERIFY(model.relationModel(0) == NULL); + QVERIFY(model.relationModel(1) == NULL); + QVERIFY(model.relationModel(2) != NULL); + QVERIFY(model.relationModel(3) == NULL); + QVERIFY(model.relationModel(4) == NULL); + + model.setRelation(3, QSqlRelation(reltest4, "id", "name")); + QVERIFY_SQL(model, select()); + + QVERIFY(model.relationModel(0) == NULL); + QVERIFY(model.relationModel(1) == NULL); + QVERIFY(model.relationModel(2) != NULL); + QVERIFY(model.relationModel(3) != NULL); + QVERIFY(model.relationModel(4) == NULL); + + QSqlTableModel *rel_model = model.relationModel(2); + QCOMPARE(rel_model->data(rel_model->index(0,1)).toString(), QString("herr")); + + //same test in JoinMode + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + QVERIFY(model.relationModel(0) == NULL); + QVERIFY(model.relationModel(1) == NULL); + QVERIFY(model.relationModel(2) != NULL); + QVERIFY(model.relationModel(3) != NULL); + QVERIFY(model.relationModel(4) == NULL); + + QSqlTableModel *rel_model2 = model.relationModel(2); + QCOMPARE(rel_model2->data(rel_model->index(0,1)).toString(), QString("herr")); +} + +void tst_QSqlRelationalTableModel::casing() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + if (db.driverName().startsWith("QSQLITE") || db.driverName().startsWith("QIBASE") || tst_Databases::isSqlServer(db)) + QSKIP("The casing test for this database is irrelevant since this database does not treat different cases as separate entities", SkipAll); + + QSqlQuery q(db); + QVERIFY_SQL( q, exec("create table " + qTableName("CASETEST1", db.driver()).toUpper() + + " (id int not null primary key, name varchar(20), title_key int, another_title_key int)")); + + if( !q.exec("create table " + qTableName("casetest1", db.driver()) + + " (ident int not null primary key, name varchar(20), title_key int)")) + QSKIP("The casing test for this database is irrelevant since this database does not treat different cases as separate entities", SkipAll); + + QVERIFY_SQL( q, exec("insert into " + qTableName("CASETEST1", db.driver()).toUpper() + " values(1, 'harry', 1, 2)")); + QVERIFY_SQL( q, exec("insert into " + qTableName("CASETEST1", db.driver()).toUpper() + " values(2, 'trond', 2, 1)")); + QVERIFY_SQL( q, exec("insert into " + qTableName("CASETEST1", db.driver()).toUpper() + " values(3, 'vohi', 1, 2)")); + QVERIFY_SQL( q, exec("insert into " + qTableName("CASETEST1", db.driver()).toUpper() + " values(4, 'boris', 2, 2)")); + QVERIFY_SQL( q, exec("insert into " + qTableName("casetest1", db.driver()) + " values(1, 'jerry', 1)")); + QVERIFY_SQL( q, exec("insert into " + qTableName("casetest1", db.driver()) + " values(2, 'george', 2)")); + QVERIFY_SQL( q, exec("insert into " + qTableName("casetest1", db.driver()) + " values(4, 'kramer', 2)")); + + if (db.driverName().startsWith("QOCI")) { + //try an owner that doesn't exist + QSqlRecord rec = db.driver()->record("doug." + qTableName("CASETEST1", db.driver()).toUpper()); + QCOMPARE( rec.count(), 0); + + //try an owner that does exist + rec = db.driver()->record(db.userName() + "." + qTableName("CASETEST1", db.driver()).toUpper()); + QCOMPARE( rec.count(), 4); + } + QSqlRecord rec = db.driver()->record(qTableName("CASETEST1", db.driver()).toUpper()); + QCOMPARE( rec.count(), 4); + + rec = db.driver()->record(qTableName("casetest1", db.driver())); + QCOMPARE( rec.count(), 3); + + QSqlTableModel upperCaseModel(0, db); + upperCaseModel.setTable(qTableName("CASETEST1", db.driver()).toUpper()); + + QCOMPARE(upperCaseModel.tableName(),qTableName("CASETEST1",db.driver()).toUpper()); + + QVERIFY_SQL(upperCaseModel, select()); + + QCOMPARE(upperCaseModel.rowCount(), 4); + + QSqlTableModel lowerCaseModel(0, db); + lowerCaseModel.setTable(qTableName("casetest1", db.driver())); + QCOMPARE(lowerCaseModel.tableName(), qTableName("casetest1",db.driver())); + QVERIFY_SQL(lowerCaseModel, select()); + + QCOMPARE(lowerCaseModel.rowCount(), 3); + + QSqlRelationalTableModel model(0, db); + model.setTable(qTableName("CASETEST1", db.driver()).toUpper()); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); +} + +void tst_QSqlRelationalTableModel::escapedRelations() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + recreateTestTables(db); + + QSqlRelationalTableModel model(0, db); + model.setTable(reltest1); + + //try with relation table name quoted + if (db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2")) { + model.setRelation(2, QSqlRelation(db.driver()->escapeIdentifier(reltest2.toUpper(),QSqlDriver::TableName), + "tid", + "title")); + } else { + model.setRelation(2, QSqlRelation(db.driver()->escapeIdentifier(reltest2,QSqlDriver::TableName), + "tid", + "title")); + + } + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + + //try with index column quoted + model.setJoinMode(QSqlRelationalTableModel::InnerJoin); + if (db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2")) { + model.setRelation(2, QSqlRelation(reltest2, + db.driver()->escapeIdentifier("tid", QSqlDriver::FieldName).toUpper(), + "title")); + } else { + model.setRelation(2, QSqlRelation(reltest2, + db.driver()->escapeIdentifier("tid", QSqlDriver::FieldName), + "title")); + } + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + + //try with display column quoted + model.setJoinMode(QSqlRelationalTableModel::InnerJoin); + if (db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2")) { + + model.setRelation(2, QSqlRelation(reltest2, + "tid", + db.driver()->escapeIdentifier("title", QSqlDriver::FieldName).toUpper())); + } else { + model.setRelation(2, QSqlRelation(reltest2, + "tid", + db.driver()->escapeIdentifier("title", QSqlDriver::FieldName))); + } + + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + + //try with tablename and index and display columns quoted in the relation + model.setJoinMode(QSqlRelationalTableModel::InnerJoin); + if (db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2")) { + model.setRelation(2, QSqlRelation(reltest2, + "tid", + db.driver()->escapeIdentifier("title", QSqlDriver::FieldName).toUpper())); + } else { + model.setRelation(2, QSqlRelation(reltest2, + "tid", + db.driver()->escapeIdentifier("title", QSqlDriver::FieldName))); + } + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); + + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("herr")); +} + +void tst_QSqlRelationalTableModel::escapedTableName() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + // set the values using OnRowChange Strategy with an escaped tablename + { + QSqlRelationalTableModel model(0, db); + + if (db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2")) { + model.setTable(db.driver()->escapeIdentifier(reltest1.toUpper(), QSqlDriver::TableName)); + } else { + model.setTable(db.driver()->escapeIdentifier(reltest1, QSqlDriver::TableName)); + } + model.setSort(0, Qt::AscendingOrder); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + + QVERIFY(model.setData(model.index(0, 1), QString("harry2"))); + QVERIFY(model.setData(model.index(0, 2), 2)); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry2")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("mister")); + + model.submit(); + + QVERIFY(model.setData(model.index(3,1), QString("boris2"))); + QVERIFY(model.setData(model.index(3, 2), 1)); + + QCOMPARE(model.data(model.index(3,1)).toString(), QString("boris2")); + QCOMPARE(model.data(model.index(3, 2)).toString(), QString("herr")); + + model.submit(); + } + { //verify values + QSqlRelationalTableModel model(0, db); + model.setTable(reltest1); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry2")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 2); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("boris2")); + QCOMPARE(model.data(model.index(3, 2)).toInt(), 1); + + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("mister")); + QCOMPARE(model.data(model.index(3,2)).toString(), QString("herr")); + + } + + //ok, now do same test with LeftJoin + { + QSqlRelationalTableModel model(0, db); + + if (db.driverName().startsWith("QIBASE") || db.driverName().startsWith("QOCI") || db.driverName().startsWith("QDB2")) { + model.setTable(db.driver()->escapeIdentifier(reltest1.toUpper(), QSqlDriver::TableName)); + } else { + model.setTable(db.driver()->escapeIdentifier(reltest1, QSqlDriver::TableName)); + } + model.setSort(0, Qt::AscendingOrder); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + QVERIFY(model.setData(model.index(0, 1), QString("harry2"))); + QVERIFY(model.setData(model.index(0, 2), 2)); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry2")); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("mister")); + + model.submit(); + + QVERIFY(model.setData(model.index(3,1), QString("boris2"))); + QVERIFY(model.setData(model.index(3, 2), 1)); + + QCOMPARE(model.data(model.index(3,1)).toString(), QString("boris2")); + QCOMPARE(model.data(model.index(3, 2)).toString(), QString("herr")); + + model.submit(); + } + { //verify values + QSqlRelationalTableModel model(0, db); + model.setTable(reltest1); + model.setSort(0, Qt::AscendingOrder); + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry2")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 2); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("boris2")); + QCOMPARE(model.data(model.index(3, 2)).toInt(), 1); + + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("mister")); + QCOMPARE(model.data(model.index(3,2)).toString(), QString("herr")); + + } +} + +void tst_QSqlRelationalTableModel::whiteSpaceInIdentifiers() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + if (!testWhiteSpaceNames(db.driverName())) + QSKIP("White space test irrelevant for driver", SkipAll); + QSqlRelationalTableModel model(0, db); + model.setTable(db.driver()->escapeIdentifier(qTableName("rel", __FILE__)+" test6", QSqlDriver::TableName)); + model.setSort(0, Qt::DescendingOrder); + model.setRelation(1, QSqlRelation(db.driver()->escapeIdentifier(qTableName("rel", __FILE__)+" test7", QSqlDriver::TableName), + db.driver()->escapeIdentifier("city id", QSqlDriver::FieldName), + db.driver()->escapeIdentifier("city name", QSqlDriver::FieldName))); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0,1)).toString(), QString("Washington")); + QCOMPARE(model.data(model.index(1,1)).toString(), QString("New York")); + + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0,1)).toString(), QString("Washington")); + QCOMPARE(model.data(model.index(1,1)).toString(), QString("New York")); + + model.setJoinMode(QSqlRelationalTableModel::InnerJoin); + QVERIFY_SQL(model, select()); + + QSqlRecord rec; + QSqlField f1("id", QVariant::Int); + QSqlField f2(db.driver()->escapeIdentifier("city key", QSqlDriver::FieldName), QVariant::Int); + QSqlField f3(db.driver()->escapeIdentifier("extra field", QSqlDriver::FieldName), QVariant::Int); + + f1.setValue(3); + f2.setValue(2); + f3.setValue(7); + + f1.setGenerated(true); + f2.setGenerated(true); + f3.setGenerated(true); + + rec.append(f1); + rec.append(f2); + rec.append(f3); + + QVERIFY_SQL(model, insertRecord(-1, rec)); + model.submitAll(); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("Washington")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 7); + + //TODO: For some reson setting a record using manual submit fails + //model.setEditStrategy(QSqlTableModel::OnManualSubmit); + + QSqlRecord recNew; + QSqlField f1New("id", QVariant::Int); + QSqlField f2New(db.driver()->escapeIdentifier("city key", QSqlDriver::FieldName), QVariant::Int); + QSqlField f3New(db.driver()->escapeIdentifier("extra field", QSqlDriver::FieldName), QVariant::Int); + + f1New.setValue(4); + f2New.setValue(1); + f3New.setValue(6); + + f1New.setGenerated(true); + f2New.setGenerated(true); + f3New.setGenerated(true); + + recNew.append(f1New); + recNew.append(f2New); + recNew.append(f3New); + + QVERIFY_SQL(model, setRecord(0, recNew)); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 4); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("New York")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 6); + + QVERIFY_SQL(model, submitAll()); + QCOMPARE(model.data(model.index(0, 0)).toInt(), 4); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("New York")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 6); +} + +void tst_QSqlRelationalTableModel::psqlSchemaTest() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + if(!tst_Databases::isPostgreSQL(db)) { + QSKIP("Postgresql specific test", SkipSingle); + return; + } + QSqlRelationalTableModel model(0, db); + QSqlQuery q(db); + QVERIFY_SQL(q, exec("create schema "+qTableName("QTBUG_5373", __FILE__))); + QVERIFY_SQL(q, exec("create schema "+qTableName("QTBUG_5373_s2", __FILE__))); + QVERIFY_SQL(q, exec("create table "+qTableName("QTBUG_5373", __FILE__)+"."+qTableName("document", __FILE__)+"(document_id int primary key, relatingid int, userid int)")); + QVERIFY_SQL(q, exec("create table "+qTableName("QTBUG_5373_s2", __FILE__)+"."+qTableName("user", __FILE__)+"(userid int primary key, username char(40))")); + model.setTable(qTableName("QTBUG_5373", __FILE__)+"."+qTableName("document", __FILE__)); + model.setRelation(1, QSqlRelation(qTableName("QTBUG_5373_s2", __FILE__)+"."+qTableName("user", __FILE__), "userid", "username")); + model.setRelation(2, QSqlRelation(qTableName("QTBUG_5373_s2", __FILE__)+"."+qTableName("user", __FILE__), "userid", "username")); + QVERIFY_SQL(model, select()); + + model.setJoinMode(QSqlRelationalTableModel::LeftJoin); + QVERIFY_SQL(model, select()); +} + +void tst_QSqlRelationalTableModel::selectAfterUpdate() +{ + QFETCH_GLOBAL(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlRelationalTableModel model(0, db); + model.setTable(reltest1); + model.setRelation(2, QSqlRelation(reltest2, "tid", "title")); + QVERIFY_SQL(model, select()); + QVERIFY(model.relationModel(2)->rowCount() == 2); + { + QSqlQuery q(db); + QVERIFY_SQL(q, exec("insert into " + reltest2 + " values(3, 'mrs')")); + model.relationModel(2)->select(); + } + QVERIFY(model.relationModel(2)->rowCount() == 3); + QVERIFY(model.setData(model.index(0,2), 3)); + QVERIFY(model.submitAll()); + QCOMPARE(model.data(model.index(0,2)), QVariant("mrs")); +} + +QTEST_MAIN(tst_QSqlRelationalTableModel) +#include "tst_qsqlrelationaltablemodel.moc" diff --git a/tests/auto/sql/models/qsqltablemodel/.gitignore b/tests/auto/sql/models/qsqltablemodel/.gitignore new file mode 100644 index 0000000000..460982f59c --- /dev/null +++ b/tests/auto/sql/models/qsqltablemodel/.gitignore @@ -0,0 +1 @@ +tst_qsqltablemodel diff --git a/tests/auto/sql/models/qsqltablemodel/qsqltablemodel.pro b/tests/auto/sql/models/qsqltablemodel/qsqltablemodel.pro new file mode 100644 index 0000000000..e49020f0a6 --- /dev/null +++ b/tests/auto/sql/models/qsqltablemodel/qsqltablemodel.pro @@ -0,0 +1,22 @@ +load(qttest_p4) +SOURCES += tst_qsqltablemodel.cpp + +QT += sql + +wince*: { + plugFiles.files = ../../../plugins/sqldrivers + plugFiles.path = . + DEPLOYMENT += plugFiles + LIBS += -lws2 +}else:symbian { + qt_not_deployed { + contains(S60_VERSION, 3.1)|contains(S60_VERSION, 3.2)|contains(S60_VERSION, 5.0) { + sqlite.path = /sys/bin + sqlite.files = sqlite3.dll + DEPLOYMENT += sqlite + } + } +}else { + win32:LIBS += -lws2_32 +} + diff --git a/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp b/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp new file mode 100644 index 0000000000..07efd4a3a1 --- /dev/null +++ b/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp @@ -0,0 +1,1377 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include "../../kernel/qsqldatabase/tst_databases.h" +#include <QtSql> + +const QString test(qTableName("test", __FILE__)), + test2(qTableName("test2", __FILE__)), + test3(qTableName("test3", __FILE__)); + +//TESTED_CLASS= +//TESTED_FILES= + +Q_DECLARE_METATYPE(QModelIndex) + +class tst_QSqlTableModel : public QObject +{ + Q_OBJECT + +public: + tst_QSqlTableModel(); + virtual ~tst_QSqlTableModel(); + + + void dropTestTables(); + void createTestTables(); + void recreateTestTables(); + void repopulateTestTables(); + + tst_Databases dbs; + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); +private slots: + + void select_data() { generic_data(); } + void select(); + void insertColumns_data() { generic_data_with_strategies(); } + void insertColumns(); + void submitAll_data() { generic_data(); } + void submitAll(); + void setRecord_data() { generic_data(); } + void setRecord(); + void insertRow_data() { generic_data_with_strategies(); } + void insertRow(); + void insertRecord_data() { generic_data(); } + void insertRecord(); + void insertMultiRecords_data() { generic_data(); } + void insertMultiRecords(); + void removeRow_data() { generic_data(); } + void removeRow(); + void removeRows_data() { generic_data(); } + void removeRows(); + void removeInsertedRow_data() { generic_data_with_strategies(); } + void removeInsertedRow(); + void removeInsertedRows_data() { generic_data(); } + void removeInsertedRows(); + void setFilter_data() { generic_data(); } + void setFilter(); + void setInvalidFilter_data() { generic_data(); } + void setInvalidFilter(); + + void emptyTable_data() { generic_data(); } + void emptyTable(); + void tablesAndSchemas_data() { generic_data("QPSQL"); } + void tablesAndSchemas(); + void whitespaceInIdentifiers_data() { generic_data(); } + void whitespaceInIdentifiers(); + void primaryKeyOrder_data() { generic_data("QSQLITE"); } + void primaryKeyOrder(); + + void sqlite_bigTable_data() { generic_data("QSQLITE"); } + void sqlite_bigTable(); + + // bug specific tests + void insertRecordBeforeSelect_data() { generic_data(); } + void insertRecordBeforeSelect(); + void submitAllOnInvalidTable_data() { generic_data(); } + void submitAllOnInvalidTable(); + void insertRecordsInLoop_data() { generic_data(); } + void insertRecordsInLoop(); + void sqlite_attachedDatabase_data() { generic_data("QSQLITE"); } + void sqlite_attachedDatabase(); // For task 130799 + void tableModifyWithBlank_data() { generic_data(); } + void tableModifyWithBlank(); // For mail task + + void removeColumnAndRow_data() { generic_data(); } + void removeColumnAndRow(); // task 256032 + + void insertBeforeDelete_data() { generic_data(); } + void insertBeforeDelete(); +private: + void generic_data(const QString& engine=QString()); + void generic_data_with_strategies(const QString& engine=QString()); +}; + +tst_QSqlTableModel::tst_QSqlTableModel() +{ + qRegisterMetaType<QModelIndex>("QModelIndex"); + dbs.open(); +} + +tst_QSqlTableModel::~tst_QSqlTableModel() +{ +} + +void tst_QSqlTableModel::dropTestTables() +{ + for (int i = 0; i < dbs.dbNames.count(); ++i) { + QSqlDatabase db = QSqlDatabase::database(dbs.dbNames.at(i)); + QSqlQuery q(db); + if(tst_Databases::isPostgreSQL(db)) + QVERIFY_SQL( q, exec("set client_min_messages='warning'")); + + QStringList tableNames; + tableNames << test + << test2 + << test3 + << qTableName("test4", __FILE__) + << qTableName("emptytable", __FILE__) + << qTableName("bigtable", __FILE__) + << qTableName("foo", __FILE__); + if (testWhiteSpaceNames(db.driverName())) + tableNames << qTableName("qtestw hitespace", db.driver()); + + tst_Databases::safeDropTables(db, tableNames); + + if (db.driverName().startsWith("QPSQL")) { + q.exec("DROP SCHEMA " + qTableName("testschema", __FILE__) + " CASCADE"); + } + } +} + +void tst_QSqlTableModel::createTestTables() +{ + for (int i = 0; i < dbs.dbNames.count(); ++i) { + QSqlDatabase db = QSqlDatabase::database(dbs.dbNames.at(i)); + QSqlQuery q(db); + + QVERIFY_SQL( q, exec("create table " + test + "(id int, name varchar(20), title int)")); + + QVERIFY_SQL( q, exec("create table " + test2 + "(id int, title varchar(20))")); + + QVERIFY_SQL( q, exec("create table " + test3 + "(id int, random varchar(20), randomtwo varchar(20))")); + + if(!tst_Databases::isSqlServer(db)) + QVERIFY_SQL( q, exec("create table " + qTableName("test4", __FILE__) + "(column1 varchar(50), column2 varchar(50), column3 varchar(50))")); + else + QVERIFY_SQL( q, exec("create table " + qTableName("test4", __FILE__) + "(column1 varchar(50), column2 varchar(50) NULL, column3 varchar(50))")); + + + QVERIFY_SQL( q, exec("create table " + qTableName("emptytable", __FILE__) + "(id int)")); + + if (testWhiteSpaceNames(db.driverName())) { + QString qry = "create table " + qTableName("qtestw hitespace", db.driver()) + " ("+ db.driver()->escapeIdentifier("a field", QSqlDriver::FieldName) + " int)"; + QVERIFY_SQL( q, exec(qry)); + } + } +} + +void tst_QSqlTableModel::repopulateTestTables() +{ + for (int i = 0; i < dbs.dbNames.count(); ++i) { + QSqlDatabase db = QSqlDatabase::database(dbs.dbNames.at(i)); + QSqlQuery q(db); + + q.exec("delete from " + test); + QVERIFY_SQL( q, exec("insert into " + test + " values(1, 'harry', 1)")); + QVERIFY_SQL( q, exec("insert into " + test + " values(2, 'trond', 2)")); + QVERIFY_SQL( q, exec("insert into " + test + " values(3, 'vohi', 3)")); + + q.exec("delete from " + test2); + QVERIFY_SQL( q, exec("insert into " + test2 + " values(1, 'herr')")); + QVERIFY_SQL( q, exec("insert into " + test2 + " values(2, 'mister')")); + + q.exec("delete from " + test3); + QVERIFY_SQL( q, exec("insert into " + test3 + " values(1, 'foo', 'bar')")); + QVERIFY_SQL( q, exec("insert into " + test3 + " values(2, 'baz', 'joe')")); + } +} + +void tst_QSqlTableModel::recreateTestTables() +{ + dropTestTables(); + createTestTables(); + repopulateTestTables(); +} + +void tst_QSqlTableModel::generic_data(const QString &engine) +{ + if ( dbs.fillTestTable(engine) == 0 ) { + if (engine.isEmpty()) + QSKIP( "No database drivers are available in this Qt configuration", SkipAll ); + else + QSKIP( (QString("No database drivers of type %1 are available in this Qt configuration").arg(engine)).toLocal8Bit(), SkipAll ); + } +} + +void tst_QSqlTableModel::generic_data_with_strategies(const QString &engine) +{ + if ( dbs.fillTestTableWithStrategies(engine) == 0 ) { + if (engine.isEmpty()) + QSKIP( "No database drivers are available in this Qt configuration", SkipAll ); + else + QSKIP( (QString("No database drivers of type %1 are available in this Qt configuration").arg(engine)).toLocal8Bit(), SkipAll ); + } +} + +void tst_QSqlTableModel::initTestCase() +{ + recreateTestTables(); +} + +void tst_QSqlTableModel::cleanupTestCase() +{ + dropTestTables(); + dbs.close(); +} + +void tst_QSqlTableModel::init() +{ +} + +void tst_QSqlTableModel::cleanup() +{ + repopulateTestTables(); +} + +void tst_QSqlTableModel::select() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.rowCount(), 3); + QCOMPARE(model.columnCount(), 3); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 3)), QVariant()); + + QCOMPARE(model.data(model.index(1, 0)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(1, 2)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 3)), QVariant()); + + QCOMPARE(model.data(model.index(2, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(2, 2)).toInt(), 3); + QCOMPARE(model.data(model.index(2, 3)), QVariant()); + + QCOMPARE(model.data(model.index(3, 0)), QVariant()); + QCOMPARE(model.data(model.index(3, 1)), QVariant()); + QCOMPARE(model.data(model.index(3, 2)), QVariant()); + QCOMPARE(model.data(model.index(3, 3)), QVariant()); +} + +void tst_QSqlTableModel::insertColumns() +{ + // Just like the select test, with extra stuff + QFETCH(QString, dbName); + QFETCH(int, submitpolicy_i); + QSqlTableModel::EditStrategy submitpolicy = (QSqlTableModel::EditStrategy) submitpolicy_i; + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + model.setEditStrategy(submitpolicy); + + QVERIFY_SQL(model, select()); + + QCOMPARE(model.rowCount(), 3); + QCOMPARE(model.columnCount(), 3); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 3)), QVariant()); + + QCOMPARE(model.data(model.index(1, 0)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(1, 2)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 3)), QVariant()); + + QCOMPARE(model.data(model.index(2, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(2, 2)).toInt(), 3); + QCOMPARE(model.data(model.index(2, 3)), QVariant()); + + QCOMPARE(model.data(model.index(3, 0)), QVariant()); + QCOMPARE(model.data(model.index(3, 1)), QVariant()); + QCOMPARE(model.data(model.index(3, 2)), QVariant()); + QCOMPARE(model.data(model.index(3, 3)), QVariant()); + + // Now add a column at 0 and 2 + model.insertColumn(0); + model.insertColumn(2); + + QCOMPARE(model.rowCount(), 3); + QCOMPARE(model.columnCount(), 5); + + QCOMPARE(model.data(model.index(0, 0)), QVariant()); + QCOMPARE(model.data(model.index(0, 1)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 2)), QVariant()); + QCOMPARE(model.data(model.index(0, 3)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 4)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 5)), QVariant()); + + QCOMPARE(model.data(model.index(1, 0)), QVariant()); + QCOMPARE(model.data(model.index(1, 1)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 2)), QVariant()); + QCOMPARE(model.data(model.index(1, 3)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(1, 4)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 5)), QVariant()); + + QCOMPARE(model.data(model.index(2, 0)), QVariant()); + QCOMPARE(model.data(model.index(2, 1)).toInt(), 3); + QCOMPARE(model.data(model.index(2, 2)), QVariant()); + QCOMPARE(model.data(model.index(2, 3)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(2, 4)).toInt(), 3); + QCOMPARE(model.data(model.index(2, 5)), QVariant()); + + QCOMPARE(model.data(model.index(3, 0)), QVariant()); + QCOMPARE(model.data(model.index(3, 1)), QVariant()); + QCOMPARE(model.data(model.index(3, 2)), QVariant()); + QCOMPARE(model.data(model.index(3, 3)), QVariant()); + QCOMPARE(model.data(model.index(3, 4)), QVariant()); + QCOMPARE(model.data(model.index(3, 5)), QVariant()); +} + +void tst_QSqlTableModel::setRecord() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QList<QSqlTableModel::EditStrategy> policies = QList<QSqlTableModel::EditStrategy>() << QSqlTableModel::OnFieldChange << QSqlTableModel::OnRowChange << QSqlTableModel::OnManualSubmit; + + QString Xsuffix; + foreach( QSqlTableModel::EditStrategy submitpolicy, policies) { + + QSqlTableModel model(0, db); + model.setEditStrategy((QSqlTableModel::EditStrategy)submitpolicy); + model.setTable(test3); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + for (int i = 0; i < model.rowCount(); ++i) { + QSignalSpy spy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + QSqlRecord rec = model.record(i); + rec.setValue(1, rec.value(1).toString() + 'X'); + rec.setValue(2, rec.value(2).toString() + 'X'); + QVERIFY(model.setRecord(i, rec)); + + if ((QSqlTableModel::EditStrategy)submitpolicy == QSqlTableModel::OnManualSubmit) { + // setRecord should emit dataChanged() itself for manualSubmit + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.at(0).count(), 2); + QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(0)), model.index(i, 0)); + QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(1)), model.index(i, rec.count() - 1)); + QVERIFY(model.submitAll()); + } else if ((QSqlTableModel::EditStrategy)submitpolicy == QSqlTableModel::OnRowChange && i == model.rowCount() -1) + model.submit(); + else { + // dataChanged() is not emitted when submitAll() is called + QCOMPARE(spy.count(), 2); + QCOMPARE(spy.at(0).count(), 2); + QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(0)), model.index(i, 1)); + QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(1)), model.index(i, 1)); + } + } + + Xsuffix.append('X'); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("foo").append(Xsuffix)); + QCOMPARE(model.data(model.index(0, 2)).toString(), QString("bar").append(Xsuffix)); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("baz").append(Xsuffix)); + QCOMPARE(model.data(model.index(1, 2)).toString(), QString("joe").append(Xsuffix)); + } +} + +void tst_QSqlTableModel::insertRow() +{ + QFETCH(QString, dbName); + QFETCH(int, submitpolicy_i); + QSqlTableModel::EditStrategy submitpolicy = (QSqlTableModel::EditStrategy) submitpolicy_i; + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setEditStrategy(submitpolicy); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); + QCOMPARE(model.data(model.index(1, 0)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(1, 2)).toInt(), 2); + QCOMPARE(model.data(model.index(2, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(2, 2)).toInt(), 3); + + QVERIFY(model.insertRow(2)); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); + QCOMPARE(model.data(model.index(1, 0)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(1, 2)).toInt(), 2); + QCOMPARE(model.data(model.index(2, 0)).toInt(), 0); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString()); + QCOMPARE(model.data(model.index(2, 2)).toInt(), 0); + QCOMPARE(model.data(model.index(3, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(3, 2)).toInt(), 3); + + QSqlRecord rec = model.record(1); + rec.setValue(0, 42); + rec.setValue(1, QString("francis")); + + // FieldChange updates immediately and resorts + // Row/Manual submit does not resort + QVERIFY(model.setRecord(2, rec)); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); + QCOMPARE(model.data(model.index(1, 0)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(1, 2)).toInt(), 2); + + // See comment above setRecord + if (submitpolicy == QSqlTableModel::OnFieldChange) { + QCOMPARE(model.data(model.index(2, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(2, 2)).toInt(), 3); + QCOMPARE(model.data(model.index(3, 0)).toInt(), 42); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("francis")); + QCOMPARE(model.data(model.index(3, 2)).toInt(), 2); + } else { + QCOMPARE(model.data(model.index(2, 0)).toInt(), 42); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("francis")); + QCOMPARE(model.data(model.index(2, 2)).toInt(), 2); + QCOMPARE(model.data(model.index(3, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(3, 2)).toInt(), 3); + } + + QVERIFY(model.submitAll()); + + // After the submit we should have the resorted view + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); + QCOMPARE(model.data(model.index(1, 0)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(1, 2)).toInt(), 2); + QCOMPARE(model.data(model.index(2, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(2, 2)).toInt(), 3); + QCOMPARE(model.data(model.index(3, 0)).toInt(), 42); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("francis")); + QCOMPARE(model.data(model.index(3, 2)).toInt(), 2); + +} + +void tst_QSqlTableModel::insertRecord() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QSqlRecord rec = model.record(); + rec.setValue(0, 42); + rec.setValue(1, QString("vohi")); + rec.setValue(2, 1); + QVERIFY(model.insertRecord(1, rec)); + QCOMPARE(model.rowCount(), 4); + + QCOMPARE(model.data(model.index(1, 0)).toInt(), 42); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(1, 2)).toInt(), 1); + + model.revertAll(); + model.setEditStrategy(QSqlTableModel::OnRowChange); + + QVERIFY(model.insertRecord(-1, rec)); + + QCOMPARE(model.data(model.index(3, 0)).toInt(), 42); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(3, 2)).toInt(), 1); +} + +void tst_QSqlTableModel::insertMultiRecords() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.rowCount(), 3); + + QVERIFY(model.insertRow(2)); + + QCOMPARE(model.data(model.index(2, 0)), QVariant(model.record().field(0).type())); + QCOMPARE(model.data(model.index(2, 1)), QVariant(model.record().field(1).type())); + QCOMPARE(model.data(model.index(2, 2)), QVariant(model.record().field(2).type())); + + QVERIFY(model.insertRow(3)); + QVERIFY(model.insertRow(0)); + + QCOMPARE(model.data(model.index(5, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(5, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(5, 2)).toInt(), 3); + + QVERIFY(model.setData(model.index(0, 0), QVariant(42))); + QVERIFY(model.setData(model.index(3, 0), QVariant(43))); + QVERIFY(model.setData(model.index(4, 0), QVariant(44))); + QVERIFY(model.setData(model.index(4, 1), QVariant(QLatin1String("gunnar")))); + QVERIFY(model.setData(model.index(4, 2), QVariant(1))); + + QVERIFY(model.submitAll()); + model.clear(); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(1, 0)).toInt(), 2); + QCOMPARE(model.data(model.index(2, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(3, 0)).toInt(), 42); + QCOMPARE(model.data(model.index(4, 0)).toInt(), 43); + QCOMPARE(model.data(model.index(5, 0)).toInt(), 44); + QCOMPARE(model.data(model.index(5, 1)).toString(), QString("gunnar")); + QCOMPARE(model.data(model.index(5, 2)).toInt(), 1); +} + +void tst_QSqlTableModel::submitAll() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + QVERIFY_SQL(model, select()); + + QVERIFY(model.setData(model.index(0, 1), "harry2", Qt::EditRole)); + QVERIFY(model.setData(model.index(1, 1), "trond2", Qt::EditRole)); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry2")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond2")); + + QVERIFY_SQL(model, submitAll()); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry2")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond2")); + + QVERIFY(model.setData(model.index(0, 1), "harry", Qt::EditRole)); + QVERIFY(model.setData(model.index(1, 1), "trond", Qt::EditRole)); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + + QVERIFY_SQL(model, submitAll()); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); +} + +void tst_QSqlTableModel::removeRow() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 3); + + // headerDataChanged must be emitted by the model when the edit strategy is OnManualSubmit, + // when OnFieldChange or OnRowChange it's not needed because the model will re-select. + qRegisterMetaType<Qt::Orientation>("Qt::Orientation"); + QSignalSpy headerDataChangedSpy(&model, SIGNAL(headerDataChanged(Qt::Orientation, int, int))); + + QVERIFY(model.removeRow(1)); + QCOMPARE(headerDataChangedSpy.count(), 1); + QCOMPARE(*static_cast<const Qt::Orientation *>(headerDataChangedSpy.at(0).value(0).constData()), Qt::Vertical); + QCOMPARE(headerDataChangedSpy.at(0).at(1).toInt(), 1); + QCOMPARE(headerDataChangedSpy.at(0).at(2).toInt(), 1); + QVERIFY(model.submitAll()); + QCOMPARE(model.rowCount(), 2); + + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(1, 0)).toInt(), 3); + model.clear(); + + recreateTestTables(); + + model.setTable(test); + model.setEditStrategy(QSqlTableModel::OnRowChange); + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 3); + + headerDataChangedSpy.clear(); + QVERIFY(model.removeRow(1)); + QCOMPARE(headerDataChangedSpy.count(), 0); + QCOMPARE(model.rowCount(), 2); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("vohi")); +} + +void tst_QSqlTableModel::removeRows() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + model.setEditStrategy(QSqlTableModel::OnFieldChange); + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 3); + + QSignalSpy beforeDeleteSpy(&model, SIGNAL(beforeDelete(int))); + + // Make sure wrong stuff is ok + QVERIFY(!model.removeRows(-1,1)); // negative start + QVERIFY(!model.removeRows(-1, 0)); // negative start, and zero count + QVERIFY(!model.removeRows(1, 0)); // zero count + QVERIFY(!model.removeRows(5, 1)); // past end (causes a beforeDelete to be emitted) + QVERIFY(!model.removeRows(1, 0, model.index(2, 0))); // can't pass a valid modelindex + + QVERIFY_SQL(model, removeRows(0, 2)); + QCOMPARE(beforeDeleteSpy.count(), 3); + QVERIFY(beforeDeleteSpy.at(0).at(0).toInt() == 5); + QVERIFY(beforeDeleteSpy.at(1).at(0).toInt() == 0); + QVERIFY(beforeDeleteSpy.at(2).at(0).toInt() == 1); + QCOMPARE(model.rowCount(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("vohi")); + model.clear(); + + recreateTestTables(); + model.setTable(test); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 3); + beforeDeleteSpy.clear(); + + // When the edit strategy is OnManualSubmit the beforeDelete() signal + // isn't emitted until submitAll() is called. + + QVERIFY(!model.removeRows(-1,1)); // negative start + QVERIFY(!model.removeRows(-1, 0)); // negative start, and zero count + QVERIFY(!model.removeRows(1, 0)); // zero count + QVERIFY(!model.removeRows(5, 1)); // past end (DOESN'T cause a beforeDelete to be emitted) + QVERIFY(!model.removeRows(1, 0, model.index(2, 0))); // can't pass a valid modelindex + + qRegisterMetaType<Qt::Orientation>("Qt::Orientation"); + QSignalSpy headerDataChangedSpy(&model, SIGNAL(headerDataChanged(Qt::Orientation, int, int))); + QVERIFY(model.removeRows(0, 2, QModelIndex())); + QCOMPARE(headerDataChangedSpy.count(), 2); + QCOMPARE(headerDataChangedSpy.at(0).at(1).toInt(), 0); + QCOMPARE(headerDataChangedSpy.at(0).at(2).toInt(), 0); + QCOMPARE(headerDataChangedSpy.at(1).at(1).toInt(), 1); + QCOMPARE(headerDataChangedSpy.at(1).at(2).toInt(), 1); + QCOMPARE(model.rowCount(), 3); + QVERIFY(beforeDeleteSpy.count() == 0); + QVERIFY(model.submitAll()); + QVERIFY(beforeDeleteSpy.count() == 2); + QVERIFY(beforeDeleteSpy.at(0).at(0).toInt() == 0); + QVERIFY(beforeDeleteSpy.at(1).at(0).toInt() == 1); + QCOMPARE(model.rowCount(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("vohi")); +} + +void tst_QSqlTableModel::removeInsertedRow() +{ + QFETCH(QString, dbName); + QFETCH(int, submitpolicy_i); + QSqlTableModel::EditStrategy submitpolicy = (QSqlTableModel::EditStrategy) submitpolicy_i; + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + + model.setEditStrategy(submitpolicy); + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 3); + + QVERIFY(model.insertRow(1)); + QCOMPARE(model.rowCount(), 4); + + QVERIFY(model.removeRow(1)); + QCOMPARE(model.rowCount(), 3); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); + + // Now insert a row with a null, and check that removing it also works (QTBUG-15979 etc) + model.insertRow(1); + model.setData(model.index(1,0), 55); + model.setData(model.index(1,1), QString("null columns")); + model.setData(model.index(1,2), QVariant()); + + model.submitAll(); + + QCOMPARE(model.rowCount(), 4); + QCOMPARE(model.data(model.index(3, 0)).toInt(), 55); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("null columns")); + QCOMPARE(model.data(model.index(3, 2)).isNull(), true); + + QVERIFY(model.removeRow(3)); + model.submitAll(); + QCOMPARE(model.rowCount(), 3); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); +} + +void tst_QSqlTableModel::removeInsertedRows() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); // you can't insert more than one row otherwise + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 3); + + // First put two empty rows, and remove them one by one + QVERIFY(model.insertRows(1, 2)); + QCOMPARE(model.rowCount(), 5); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString()); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString()); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(4, 1)).toString(), QString("vohi")); + + QVERIFY(model.removeRow(1)); + QCOMPARE(model.rowCount(), 4); + + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString()); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("vohi")); + + QVERIFY(model.removeRow(1)); + QCOMPARE(model.rowCount(), 3); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); + + // Now put two empty rows, and remove them all at once + QVERIFY(model.insertRows(1, 2)); + QCOMPARE(model.rowCount(), 5); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString()); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString()); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(4, 1)).toString(), QString("vohi")); + + QVERIFY(model.removeRows(1, 2)); + QCOMPARE(model.rowCount(), 3); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); + + + // Now put two empty rows, and remove one good and two empty + QVERIFY(model.insertRows(1, 2)); + QCOMPARE(model.rowCount(), 5); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString()); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString()); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(4, 1)).toString(), QString("vohi")); + + QVERIFY(model.removeRows(0, 3)); + QVERIFY(model.submitAll()); // otherwise the remove of the real row doesn't work + + QCOMPARE(model.rowCount(), 2); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("vohi")); + + // Reset back again + model.clear(); + recreateTestTables(); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); // you can't insert more than one row otherwise + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 3); + + // Now two empty and one good + QVERIFY(model.insertRows(1, 2)); + QCOMPARE(model.rowCount(), 5); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString()); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString()); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(4, 1)).toString(), QString("vohi")); + + QVERIFY(model.removeRows(1, 3)); + QVERIFY(model.submitAll()); + QCOMPARE(model.rowCount(), 2); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("vohi")); + + // Reset back again + model.clear(); + recreateTestTables(); + model.setTable(test); + model.setSort(0, Qt::AscendingOrder); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); // you can't insert more than one row otherwise + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 3); + + // one empty, one good, one empty + QVERIFY(model.insertRows(1, 1)); + QVERIFY(model.insertRows(3, 1)); + QCOMPARE(model.rowCount(), 5); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString()); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString()); + QCOMPARE(model.data(model.index(4, 1)).toString(), QString("vohi")); + + QVERIFY(model.removeRows(1, 3)); + QVERIFY(model.submitAll()); + QCOMPARE(model.rowCount(), 2); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("vohi")); +} + +void tst_QSqlTableModel::emptyTable() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + QCOMPARE(model.rowCount(), 0); + QCOMPARE(model.columnCount(), 0); + + model.setTable(qTableName("emptytable", __FILE__)); + QCOMPARE(model.rowCount(), 0); + QCOMPARE(model.columnCount(), 1); + + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 0); + QCOMPARE(model.columnCount(), 1); +} + +void tst_QSqlTableModel::tablesAndSchemas() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlQuery q(db); + q.exec("DROP SCHEMA " + qTableName("testschema", __FILE__) + " CASCADE"); + QVERIFY_SQL( q, exec("create schema " + qTableName("testschema", __FILE__))); + QString tableName = qTableName("testschema", __FILE__) + '.' + qTableName("testtable", __FILE__); + QVERIFY_SQL( q, exec("create table " + tableName + "(id int)")); + QVERIFY_SQL( q, exec("insert into " + tableName + " values(1)")); + QVERIFY_SQL( q, exec("insert into " + tableName + " values(2)")); + + QSqlTableModel model(0, db); + model.setTable(tableName); + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 2); + QCOMPARE(model.columnCount(), 1); +} + +void tst_QSqlTableModel::whitespaceInIdentifiers() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + if (!testWhiteSpaceNames(db.driverName())) + QSKIP("DBMS doesn't support whitespaces in identifiers", SkipSingle); + + QString tableName = qTableName("qtestw hitespace", db.driver()); + + QSqlTableModel model(0, db); + model.setTable(tableName); + QVERIFY_SQL(model, select()); +} + +void tst_QSqlTableModel::primaryKeyOrder() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlQuery q(db); + + if(tst_Databases::isPostgreSQL(db)) + QVERIFY_SQL( q, exec("set client_min_messages='warning'")); + + QVERIFY_SQL( q, exec("create table "+qTableName("foo", __FILE__)+"(a varchar(20), id int not null primary key, b varchar(20))")); + + QSqlTableModel model(0, db); + model.setTable(qTableName("foo", __FILE__)); + + QSqlIndex pk = model.primaryKey(); + QCOMPARE(pk.count(), 1); + QCOMPARE(pk.fieldName(0), QLatin1String("id")); + + QVERIFY(model.insertRow(0)); + QVERIFY(model.setData(model.index(0, 0), "hello")); + QVERIFY(model.setData(model.index(0, 1), 42)); + QVERIFY(model.setData(model.index(0, 2), "blah")); + QVERIFY_SQL(model, submitAll()); + + QVERIFY(model.setData(model.index(0, 1), 43)); + QVERIFY_SQL(model, submitAll()); + + QCOMPARE(model.data(model.index(0, 1)).toInt(), 43); +} + +void tst_QSqlTableModel::setInvalidFilter() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + // set an invalid filter, make sure it fails + QSqlTableModel model(0, db); + model.setTable(test); + model.setFilter("blahfahsel"); + + QCOMPARE(model.filter(), QString("blahfahsel")); + QVERIFY(!model.select()); + + // set a valid filter later, make sure if passes + model.setFilter("id = 1"); + QVERIFY_SQL(model, select()); +} + +void tst_QSqlTableModel::setFilter() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setTable(test); + model.setFilter("id = 1"); + QCOMPARE(model.filter(), QString("id = 1")); + QVERIFY_SQL(model, select()); + + QCOMPARE(model.rowCount(), 1); + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + + QSignalSpy rowsRemovedSpy(&model, SIGNAL(rowsRemoved(QModelIndex,int,int))); + QSignalSpy rowsAboutToBeRemovedSpy(&model, + SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy rowsInsertedSpy(&model, SIGNAL(rowsInserted(QModelIndex,int,int))); + QSignalSpy rowsAboutToBeInsertedSpy(&model, + SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + model.setFilter("id = 2"); + + // check the signals + QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1); + QCOMPARE(rowsRemovedSpy.count(), 1); + QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1); + QCOMPARE(rowsInsertedSpy.count(), 1); + QList<QVariant> args = rowsAboutToBeRemovedSpy.takeFirst(); + QCOMPARE(args.count(), 3); + QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), QModelIndex()); + QCOMPARE(args.at(1).toInt(), 0); + QCOMPARE(args.at(2).toInt(), 0); + args = rowsRemovedSpy.takeFirst(); + QCOMPARE(args.count(), 3); + QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), QModelIndex()); + QCOMPARE(args.at(1).toInt(), 0); + QCOMPARE(args.at(2).toInt(), 0); + args = rowsInsertedSpy.takeFirst(); + QCOMPARE(args.count(), 3); + QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), QModelIndex()); + QCOMPARE(args.at(1).toInt(), 0); + QCOMPARE(args.at(2).toInt(), 0); + args = rowsAboutToBeInsertedSpy.takeFirst(); + QCOMPARE(args.count(), 3); + QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), QModelIndex()); + QCOMPARE(args.at(1).toInt(), 0); + QCOMPARE(args.at(2).toInt(), 0); + + QCOMPARE(model.rowCount(), 1); + QCOMPARE(model.data(model.index(0, 0)).toInt(), 2); +} + +void tst_QSqlTableModel::sqlite_bigTable() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + const QString bigtable(qTableName("bigtable", __FILE__)); + + bool hasTransactions = db.driver()->hasFeature(QSqlDriver::Transactions); + if (hasTransactions) QVERIFY(db.transaction()); + QSqlQuery q(db); + QVERIFY_SQL( q, exec("create table "+bigtable+"(id int primary key, name varchar)")); + QVERIFY_SQL( q, prepare("insert into "+bigtable+"(id, name) values (?, ?)")); + QTime startTime; + startTime.start(); + for (int i = 0; i < 10000; ++i) { + q.addBindValue(i); + q.addBindValue(QString::number(i)); + if(i%1000 == 0 && startTime.elapsed() > 5000) + qDebug() << i << "records written"; + QVERIFY_SQL( q, exec()); + } + q.clear(); + if (hasTransactions) QVERIFY(db.commit()); + + QSqlTableModel model(0, db); + model.setTable(bigtable); + QVERIFY_SQL(model, select()); + + QSqlRecord rec = model.record(); + rec.setValue("id", 424242); + rec.setValue("name", "Guillaume"); + QVERIFY_SQL(model, insertRecord(-1, rec)); + + model.clear(); +} + +// For task 118547: couldn't insert records unless select() +// had first been called. +void tst_QSqlTableModel::insertRecordBeforeSelect() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setTable(test); + QCOMPARE(model.lastError().type(), QSqlError::NoError); + + QSqlRecord buffer = model.record(); + buffer.setValue("id", 13); + buffer.setValue("name", QString("The Lion King")); + buffer.setValue("title", 0); + QVERIFY_SQL(model, insertRecord(-1, buffer)); + + buffer.setValue("id", 26); + buffer.setValue("name", QString("T. Leary")); + buffer.setValue("title", 0); + QVERIFY_SQL(model, insertRecord(1, buffer)); + + int rowCount = model.rowCount(); + model.clear(); + QCOMPARE(model.rowCount(), 0); + + QSqlTableModel model2(0, db); + model2.setTable(test); + QVERIFY_SQL(model2, select()); + QCOMPARE(model2.rowCount(), rowCount); +} + +// For task 118547: set errors if table doesn't exist and if records +// are inserted and submitted on a non-existing table. +void tst_QSqlTableModel::submitAllOnInvalidTable() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + + // setTable returns a void, so the error can only be caught by + // manually checking lastError(). ### Qt5: This should be changed! + model.setTable(qTableName("invalidTable", __FILE__)); + QCOMPARE(model.lastError().type(), QSqlError::StatementError); + + // This will give us an empty record which is expected behavior + QSqlRecord buffer = model.record(); + buffer.setValue("bogus", 1000); + buffer.setValue("bogus2", QString("I will go nowhere!")); + + // Inserting the record into the *model* will work (OnManualSubmit) + QVERIFY_SQL(model, insertRecord(-1, buffer)); + + // The submit and select shall fail because the table doesn't exist + QEXPECT_FAIL("", "The table doesn't exist: submitAll() shall fail", + Continue); + QVERIFY_SQL(model, submitAll()); + QEXPECT_FAIL("", "The table doesn't exist: select() shall fail", + Continue); + QVERIFY_SQL(model, select()); +} + +// For task 147575: the rowsRemoved signal emitted from the model was lying +void tst_QSqlTableModel::insertRecordsInLoop() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setTable(test); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + model.select(); + + QSqlRecord record = model.record(); + record.setValue(0, 10); + record.setValue(1, "Testman"); + record.setValue(2, 1); + + QSignalSpy spyRowsRemoved(&model, SIGNAL(rowsRemoved(const QModelIndex &, int, int))); + QSignalSpy spyRowsInserted(&model, SIGNAL(rowsInserted(const QModelIndex &, int, int))); + for (int i = 0; i < 10; i++) { + QVERIFY(model.insertRecord(model.rowCount(), record)); + QCOMPARE(spyRowsInserted.at(i).at(1).toInt(), i+3); // The table already contains three rows + QCOMPARE(spyRowsInserted.at(i).at(2).toInt(), i+3); + } + model.submitAll(); // submitAll() calls select() which clears and repopulates the table + + int firstRowIndex = 0, lastRowIndex = 12; + QCOMPARE(spyRowsRemoved.count(), 1); + QCOMPARE(spyRowsRemoved.at(0).at(1).toInt(), firstRowIndex); + QCOMPARE(spyRowsRemoved.at(0).at(2).toInt(), lastRowIndex); + + QCOMPARE(spyRowsInserted.at(10).at(1).toInt(), firstRowIndex); + QCOMPARE(spyRowsInserted.at(10).at(2).toInt(), lastRowIndex); + QCOMPARE(spyRowsInserted.count(), 11); + + QCOMPARE(model.rowCount(), 13); + QCOMPARE(model.columnCount(), 3); +} + +void tst_QSqlTableModel::sqlite_attachedDatabase() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + if(db.databaseName() == ":memory:") + QSKIP(":memory: database, skipping test", SkipSingle); + + QSqlDatabase attachedDb = QSqlDatabase::cloneDatabase(db, db.driverName() + QLatin1String("attached")); + attachedDb.setDatabaseName(db.databaseName()+QLatin1String("attached.dat")); + QVERIFY_SQL(attachedDb, open()); + QSqlQuery q(attachedDb); + tst_Databases::safeDropTables(attachedDb, QStringList() << "atest" << "atest2"); + QVERIFY_SQL( q, exec("CREATE TABLE atest(id int, text varchar(20))")); + QVERIFY_SQL( q, exec("CREATE TABLE atest2(id int, text varchar(20))")); + QVERIFY_SQL( q, exec("INSERT INTO atest VALUES(1, 'attached-atest')")); + QVERIFY_SQL( q, exec("INSERT INTO atest2 VALUES(2, 'attached-atest2')")); + + QSqlQuery q2(db); + tst_Databases::safeDropTable(db, "atest"); + QVERIFY_SQL(q2, exec("CREATE TABLE atest(id int, text varchar(20))")); + QVERIFY_SQL(q2, exec("INSERT INTO atest VALUES(3, 'main')")); + QVERIFY_SQL(q2, exec("ATTACH DATABASE \""+attachedDb.databaseName()+"\" as adb")); + + // This should query the table in the attached database (schema supplied) + QSqlTableModel model(0, db); + model.setTable("adb.atest"); + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 1); + QCOMPARE(model.data(model.index(0, 0), Qt::DisplayRole).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1), Qt::DisplayRole).toString(), QLatin1String("attached-atest")); + + // This should query the table in the attached database (unique tablename) + model.setTable("atest2"); + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 1); + QCOMPARE(model.data(model.index(0, 0), Qt::DisplayRole).toInt(), 2); + QCOMPARE(model.data(model.index(0, 1), Qt::DisplayRole).toString(), QLatin1String("attached-atest2")); + + // This should query the table in the main database (tables in main db has 1st priority) + model.setTable("atest"); + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 1); + QCOMPARE(model.data(model.index(0, 0), Qt::DisplayRole).toInt(), 3); + QCOMPARE(model.data(model.index(0, 1), Qt::DisplayRole).toString(), QLatin1String("main")); +} + + +void tst_QSqlTableModel::tableModifyWithBlank() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setTable(qTableName("test4", __FILE__)); + model.select(); + + //generate a time stamp for the test. Add one second to the current time to make sure + //it is different than the QSqlQuery test. + QString timeString=QDateTime::currentDateTime().addSecs(1).toString(Qt::ISODate); + + //insert a new row, with column0 being the timestamp. + //Should be equivalent to QSqlQuery INSERT INTO... command) + QVERIFY_SQL(model, insertRow(0)); + QVERIFY_SQL(model, setData(model.index(0,0),timeString)); + QVERIFY_SQL(model, submitAll()); + + //set a filter on the table so the only record we get is the one we just made + //I could just do another setData command, but I want to make sure the TableModel + //matches exactly what is stored in the database + model.setFilter("column1='"+timeString+"'"); //filter to get just the newly entered row + QVERIFY_SQL(model, select()); + + //Make sure we only get one record, and that it is the one we just made + QCOMPARE(model.rowCount(), 1); //verify only one entry + QCOMPARE(model.record(0).value(0).toString(), timeString); //verify correct record + + //At this point we know that the initial value (timestamp) was succsefully stored in the database + //Attempt to modify the data in the new record + //equivalent to query.exec("update test set column3="... command in direct test + //set the data in the first column to "col1ModelData" + QVERIFY_SQL(model, setData(model.index(0,1), "col1ModelData")); + + //do a quick check to make sure that the setData command properly set the value in the model + QCOMPARE(model.record(0).value(1).toString(), QLatin1String("col1ModelData")); + + //submit the changed data to the database + //This is where I have been getting errors. + QVERIFY_SQL(model, submitAll()); + + //make sure the model has the most current data for our record + QVERIFY_SQL(model, select()); + + //verify that our new record was the only record returned + QCOMPARE(model.rowCount(), 1); + + //And that the record returned is, in fact, our test record. + QCOMPARE(model.record(0).value(0).toString(), timeString); + + //Make sure the value of the first column matches what we set it to previously. + QCOMPARE(model.record(0).value(1).toString(), QLatin1String("col1ModelData")); +} + +void tst_QSqlTableModel::removeColumnAndRow() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlTableModel model(0, db); + model.setTable(test); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + QVERIFY_SQL(model, select()); + QCOMPARE(model.rowCount(), 3); + QCOMPARE(model.columnCount(), 3); + + QVERIFY(model.removeColumn(0)); + QVERIFY(model.removeRow(0)); + QVERIFY(model.submitAll()); + QCOMPARE(model.rowCount(), 2); + QCOMPARE(model.columnCount(), 2); + + // check with another table because the model has been modified + // but not the sql table + QSqlTableModel model2(0, db); + model2.setTable(test); + QVERIFY_SQL(model2, select()); + QCOMPARE(model2.rowCount(), 2); + QCOMPARE(model2.columnCount(), 3); +} + +void tst_QSqlTableModel::insertBeforeDelete() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlQuery q(db); + QVERIFY_SQL( q, exec("insert into " + test + " values(9, 'andrew', 9)")); + QVERIFY_SQL( q, exec("insert into " + test + " values(10, 'justin', 10)")); + + QSqlTableModel model(0, db); + model.setTable(test); + model.setEditStrategy(QSqlTableModel::OnManualSubmit); + QVERIFY_SQL(model, select()); + + QSqlRecord rec = model.record(); + rec.setValue(0, 4); + rec.setValue(1, QString("bill")); + rec.setValue(2, 4); + QVERIFY_SQL(model, insertRecord(4, rec)); + + QVERIFY_SQL(model, removeRow(5)); + QVERIFY_SQL(model, submitAll()); + QCOMPARE(model.rowCount(), 5); +} + +QTEST_MAIN(tst_QSqlTableModel) +#include "tst_qsqltablemodel.moc" |