1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtSql module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qsqlrelationaltablemodel.h"
41 
42 #include "qhash.h"
43 #include "qstringlist.h"
44 #include "qsqldatabase.h"
45 #include "qsqldriver.h"
46 #include "qsqlerror.h"
47 #include "qsqlfield.h"
48 #include "qsqlindex.h"
49 #include "qsqlquery.h"
50 #include "qsqlrecord.h"
51 
52 #include "qsqltablemodel_p.h"
53 
54 #include "qdebug.h"
55 
56 QT_BEGIN_NAMESPACE
57 
58 class QSqlRelationalTableModelSql: public QSqlTableModelSql
59 {
60 public:
relTablePrefix(int i)61     inline const static QString relTablePrefix(int i) { return QString::number(i).prepend(QLatin1String("relTblAl_")); }
62 };
63 
64 typedef QSqlRelationalTableModelSql Sql;
65 
66 /*!
67     \class QSqlRelation
68     \inmodule QtSql
69     \brief The QSqlRelation class stores information about an SQL foreign key.
70 
71     QSqlRelation is a helper class for QSqlRelationalTableModel. See
72     QSqlRelationalTableModel::setRelation() and
73     QSqlRelationalTableModel::relation() for details.
74 
75     \sa QSqlRelationalTableModel, QSqlRelationalDelegate,
76         {Relational Table Model Example}
77 */
78 
79 /*!
80     \fn QSqlRelation::QSqlRelation()
81 
82     Constructs an invalid QSqlRelation object.
83 
84     For such an object, the tableName(), indexColumn(), and
85     displayColumn() functions return an empty string.
86 
87     \sa isValid()
88 */
89 
90 /*!
91     \fn QSqlRelation::QSqlRelation(const QString &tableName, const QString &indexColumn,
92                                    const QString &displayColumn)
93 
94     Constructs a QSqlRelation object, where \a tableName is the SQL
95     table name to which a foreign key refers, \a indexColumn is the
96     foreign key, and \a displayColumn is the field that should be
97     presented to the user.
98 
99     \sa tableName(), indexColumn(), displayColumn()
100 */
101 
102 /*!
103   \fn void QSqlRelation::swap(QSqlRelation &other)
104 
105   Swaps \c this with \a other.
106  */
107 
108 /*!
109     \fn QString QSqlRelation::tableName() const
110 
111     Returns the name of the table to which a foreign key refers.
112 */
113 
114 /*!
115     \fn QString QSqlRelation::indexColumn() const
116 
117     Returns the index column from table tableName() to which a
118     foreign key refers.
119 */
120 
121 /*!
122     \fn QString QSqlRelation::displayColumn() const
123 
124     Returns the column from table tableName() that should be
125     presented to the user instead of a foreign key.
126 */
127 
128 /*!
129     \fn bool QSqlRelation::isValid() const
130 
131     Returns \c true if the QSqlRelation object is valid; otherwise
132     returns \c false.
133 */
134 
135 class QRelatedTableModel;
136 
137 struct QRelation
138 {
139     public:
QRelationQRelation140         QRelation(): model(nullptr), m_parent(nullptr), m_dictInitialized(false) {}
141         void init(QSqlRelationalTableModel *parent, const QSqlRelation &relation);
142 
143         void populateModel();
144 
145         bool isDictionaryInitialized();
146         void populateDictionary();
147         void clearDictionary();
148 
149         void clear();
150         bool isValid();
151 
152         QSqlRelation rel;
153         QRelatedTableModel *model;
154         QHash<QString, QVariant> dictionary;//maps keys to display values
155 
156     private:
157         QSqlRelationalTableModel *m_parent;
158         bool m_dictInitialized;
159 };
160 
161 class QRelatedTableModel : public QSqlTableModel
162 {
163 public:
164     QRelatedTableModel(QRelation *rel, QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase());
165     bool select() override;
166 private:
167     bool firstSelect;
168     QRelation *relation;
169 };
170 /*
171     A QRelation must be initialized before it is considered valid.
172     Note: population of the model and dictionary are kept separate
173           from initialization, and are populated on an as needed basis.
174 */
init(QSqlRelationalTableModel * parent,const QSqlRelation & relation)175 void QRelation::init(QSqlRelationalTableModel *parent, const QSqlRelation &relation)
176 {
177     Q_ASSERT(parent != nullptr);
178     m_parent = parent;
179     rel = relation;
180 }
181 
populateModel()182 void QRelation::populateModel()
183 {
184     if (!isValid())
185         return;
186     Q_ASSERT(m_parent != nullptr);
187 
188     if (!model) {
189         model = new QRelatedTableModel(this, m_parent, m_parent->database());
190         model->setTable(rel.tableName());
191         model->select();
192     }
193 }
194 
isDictionaryInitialized()195 bool QRelation::isDictionaryInitialized()
196 {
197     return m_dictInitialized;
198 }
199 
populateDictionary()200 void QRelation::populateDictionary()
201 {
202     if (!isValid())
203         return;
204 
205     if (model ==  nullptr)
206         populateModel();
207 
208     QSqlRecord record;
209     QString indexColumn;
210     QString displayColumn;
211     for (int i=0; i < model->rowCount(); ++i) {
212         record = model->record(i);
213 
214         indexColumn = rel.indexColumn();
215         if (m_parent->database().driver()->isIdentifierEscaped(indexColumn, QSqlDriver::FieldName))
216             indexColumn = m_parent->database().driver()->stripDelimiters(indexColumn, QSqlDriver::FieldName);
217 
218         displayColumn = rel.displayColumn();
219         if (m_parent->database().driver()->isIdentifierEscaped(displayColumn, QSqlDriver::FieldName))
220             displayColumn = m_parent->database().driver()->stripDelimiters(displayColumn, QSqlDriver::FieldName);
221 
222         dictionary[record.field(indexColumn).value().toString()] =
223             record.field(displayColumn).value();
224     }
225     m_dictInitialized = true;
226 }
227 
clearDictionary()228 void QRelation::clearDictionary()
229 {
230     dictionary.clear();
231     m_dictInitialized = false;
232 }
233 
clear()234 void QRelation::clear()
235 {
236     delete model;
237     model = nullptr;
238     clearDictionary();
239 }
240 
isValid()241 bool QRelation::isValid()
242 {
243     return (rel.isValid() && m_parent != nullptr);
244 }
245 
246 
247 
QRelatedTableModel(QRelation * rel,QObject * parent,QSqlDatabase db)248 QRelatedTableModel::QRelatedTableModel(QRelation *rel, QObject *parent, QSqlDatabase db) :
249     QSqlTableModel(parent, db), firstSelect(true), relation(rel)
250 {
251 }
252 
select()253 bool QRelatedTableModel::select()
254 {
255     if (firstSelect) {
256         firstSelect = false;
257         return QSqlTableModel::select();
258     }
259     relation->clearDictionary();
260     bool res = QSqlTableModel::select();
261     if (res)
262         relation->populateDictionary();
263     return res;
264 }
265 
266 
267 class QSqlRelationalTableModelPrivate: public QSqlTableModelPrivate
268 {
269     Q_DECLARE_PUBLIC(QSqlRelationalTableModel)
270 public:
QSqlRelationalTableModelPrivate()271     QSqlRelationalTableModelPrivate()
272         : QSqlTableModelPrivate(),
273         joinMode( QSqlRelationalTableModel::InnerJoin )
274     {}
275     QString fullyQualifiedFieldName(const QString &tableName, const QString &fieldName) const;
276 
277     int nameToIndex(const QString &name) const override;
278     mutable QVector<QRelation> relations;
279     QSqlRecord baseRec; // the record without relations
280     void clearChanges();
281     void clearCache() override;
282     void revertCachedRow(int row) override;
283 
284     void translateFieldNames(QSqlRecord &values) const;
285     QSqlRelationalTableModel::JoinMode joinMode;
286 };
287 
clearChanges()288 void QSqlRelationalTableModelPrivate::clearChanges()
289 {
290     for (int i = 0; i < relations.count(); ++i) {
291         QRelation &rel = relations[i];
292         rel.clear();
293     }
294 }
295 
revertCachedRow(int row)296 void QSqlRelationalTableModelPrivate::revertCachedRow(int row)
297 {
298     QSqlTableModelPrivate::revertCachedRow(row);
299 }
300 
nameToIndex(const QString & name) const301 int QSqlRelationalTableModelPrivate::nameToIndex(const QString &name) const
302 {
303     const QString fieldname = strippedFieldName(name);
304     int idx = baseRec.indexOf(fieldname);
305     if (idx == -1) {
306         // If the name is an alias we can find it here.
307         idx = QSqlTableModelPrivate::nameToIndex(name);
308     }
309     return idx;
310 }
311 
clearCache()312 void QSqlRelationalTableModelPrivate::clearCache()
313 {
314     for (int i = 0; i < relations.count(); ++i)
315         relations[i].clearDictionary();
316 
317     QSqlTableModelPrivate::clearCache();
318 }
319 
320 /*!
321     \class QSqlRelationalTableModel
322     \brief The QSqlRelationalTableModel class provides an editable
323     data model for a single database table, with foreign key support.
324 
325     \ingroup database
326     \inmodule QtSql
327 
328     QSqlRelationalTableModel acts like QSqlTableModel, but allows
329     columns to be set as foreign keys into other database tables.
330 
331     \table
332     \row \li \inlineimage noforeignkeys.png
333          \li \inlineimage foreignkeys.png
334     \endtable
335 
336     The screenshot on the left shows a plain QSqlTableModel in a
337     QTableView. Foreign keys (\c city and \c country) aren't resolved
338     to human-readable values. The screenshot on the right shows a
339     QSqlRelationalTableModel, with foreign keys resolved into
340     human-readable text strings.
341 
342     The following code snippet shows how the QSqlRelationalTableModel
343     was set up:
344 
345     \snippet relationaltablemodel/relationaltablemodel.cpp 0
346     \codeline
347     \snippet relationaltablemodel/relationaltablemodel.cpp 1
348     \snippet relationaltablemodel/relationaltablemodel.cpp 2
349 
350     The setRelation() function calls establish a relationship between
351     two tables. The first call specifies that column 2 in table \c
352     employee is a foreign key that maps with field \c id of table \c
353     city, and that the view should present the \c{city}'s \c name
354     field to the user. The second call does something similar with
355     column 3.
356 
357     If you use a read-write QSqlRelationalTableModel, you probably
358     want to use QSqlRelationalDelegate on the view. Unlike the default
359     delegate, QSqlRelationalDelegate provides a combobox for fields
360     that are foreign keys into other tables. To use the class, simply
361     call QAbstractItemView::setItemDelegate() on the view with an
362     instance of QSqlRelationalDelegate:
363 
364     \snippet relationaltablemodel/relationaltablemodel.cpp 4
365 
366     The \l{relationaltablemodel} example illustrates how to use
367     QSqlRelationalTableModel in conjunction with
368     QSqlRelationalDelegate to provide tables with foreign key
369     support.
370 
371     \image relationaltable.png
372 
373     Notes:
374 
375     \list
376     \li The table must have a primary key declared.
377     \li The table's primary key may not contain a relation to
378        another table.
379     \li If a relational table contains keys that refer to non-existent
380        rows in the referenced table, the rows containing the invalid
381        keys will not be exposed through the model. The user or the
382        database is responsible for keeping referential integrity.
383     \li If a relation's display column name is also used as a column
384        name in the relational table, or if it is used as display column
385        name in more than one relation it will be aliased. The alias is
386        the relation's table name, display column name and a unique id
387        joined by an underscore (e.g. tablename_columnname_id).
388        QSqlRecord::fieldName() will return the aliased column name.
389        All occurrences of the duplicate display column name are aliased when
390        duplication is detected, but no aliasing is done to the column
391        names in the main table. The aliasing doesn't affect
392        QSqlRelation, so QSqlRelation::displayColumn() will return the
393        original display column name.
394     \li The reference table name is aliased. The alias is the word "relTblAl"
395        and the relationed column index joined by an underscore
396        (e.g. relTblAl_2). The alias can be used to filter the table
397        (For example, setFilter("relTblAl_2='Oslo' OR
398        relTblAl_3='USA'")).
399     \li When using setData() the role should always be Qt::EditRole,
400        and when using data() the role should always be Qt::DisplayRole.
401     \endlist
402 
403     \sa QSqlRelation, QSqlRelationalDelegate,
404         {Relational Table Model Example}
405 */
406 
407 
408 /*!
409     Creates an empty QSqlRelationalTableModel and sets the parent to \a parent
410     and the database connection to \a db. If \a db is not valid, the
411     default database connection will be used.
412 */
QSqlRelationalTableModel(QObject * parent,QSqlDatabase db)413 QSqlRelationalTableModel::QSqlRelationalTableModel(QObject *parent, QSqlDatabase db)
414     : QSqlTableModel(*new QSqlRelationalTableModelPrivate, parent, db)
415 {
416 }
417 
418 /*!
419     Destroys the object and frees any allocated resources.
420 */
~QSqlRelationalTableModel()421 QSqlRelationalTableModel::~QSqlRelationalTableModel()
422 {
423 }
424 
425 /*!
426     \reimp
427 */
data(const QModelIndex & index,int role) const428 QVariant QSqlRelationalTableModel::data(const QModelIndex &index, int role) const
429 {
430     Q_D(const QSqlRelationalTableModel);
431 
432     if (role == Qt::DisplayRole && index.column() >= 0 && index.column() < d->relations.count() &&
433             d->relations.value(index.column()).isValid()) {
434         QRelation &relation = d->relations[index.column()];
435         if (!relation.isDictionaryInitialized())
436             relation.populateDictionary();
437 
438         //only perform a dictionary lookup for the display value
439         //when the value at index has been changed or added.
440         //At an unmodified index, the underlying model will
441         //already have the correct display value.
442         if (d->strategy != OnFieldChange) {
443             const QSqlTableModelPrivate::ModifiedRow row = d->cache.value(index.row());
444             if (row.op() != QSqlTableModelPrivate::None && row.rec().isGenerated(index.column())) {
445                 if (d->strategy == OnManualSubmit || row.op() != QSqlTableModelPrivate::Delete) {
446                     QVariant v = row.rec().value(index.column());
447                     if (v.isValid())
448                         return relation.dictionary[v.toString()];
449                 }
450             }
451         }
452     }
453     return QSqlTableModel::data(index, role);
454 }
455 
456 /*!
457     Sets the data for the \a role in the item with the specified \a
458     index to the \a value given. Depending on the edit strategy, the
459     value might be applied to the database at once, or it may be
460     cached in the model.
461 
462     Returns \c true if the value could be set, or false on error (for
463     example, if \a index is out of bounds).
464 
465     For relational columns, \a value must be the index, not the
466     display value. The index must also exist in the referenced
467     table, otherwise the function returns \c false.
468 
469     \sa editStrategy(), data(), submit(), revertRow()
470 */
setData(const QModelIndex & index,const QVariant & value,int role)471 bool QSqlRelationalTableModel::setData(const QModelIndex &index, const QVariant &value,
472                                        int role)
473 {
474     Q_D(QSqlRelationalTableModel);
475     if ( role == Qt::EditRole && index.column() > 0 && index.column() < d->relations.count()
476             && d->relations.value(index.column()).isValid()) {
477         QRelation &relation = d->relations[index.column()];
478         if (!relation.isDictionaryInitialized())
479             relation.populateDictionary();
480         if (!relation.dictionary.contains(value.toString()))
481             return false;
482     }
483     return QSqlTableModel::setData(index, value, role);
484 }
485 
486 /*!
487     Lets the specified \a column be a foreign index specified by \a relation.
488 
489     Example:
490 
491     \snippet relationaltablemodel/relationaltablemodel.cpp 0
492     \codeline
493     \snippet relationaltablemodel/relationaltablemodel.cpp 1
494 
495     The setRelation() call specifies that column 2 in table \c
496     employee is a foreign key that maps with field \c id of table \c
497     city, and that the view should present the \c{city}'s \c name
498     field to the user.
499 
500     Note: The table's primary key may not contain a relation to another table.
501 
502     \sa relation()
503 */
setRelation(int column,const QSqlRelation & relation)504 void QSqlRelationalTableModel::setRelation(int column, const QSqlRelation &relation)
505 {
506     Q_D(QSqlRelationalTableModel);
507     if (column < 0)
508         return;
509     if (d->relations.size() <= column)
510         d->relations.resize(column + 1);
511     d->relations[column].init(this, relation);
512 }
513 
514 /*!
515     Returns the relation for the column \a column, or an invalid
516     relation if no relation is set.
517 
518     \sa setRelation(), QSqlRelation::isValid()
519 */
relation(int column) const520 QSqlRelation QSqlRelationalTableModel::relation(int column) const
521 {
522     Q_D(const QSqlRelationalTableModel);
523     return d->relations.value(column).rel;
524 }
525 
fullyQualifiedFieldName(const QString & tableName,const QString & fieldName) const526 QString QSqlRelationalTableModelPrivate::fullyQualifiedFieldName(const QString &tableName,
527                                                                  const QString &fieldName) const
528 {
529     QString ret;
530     ret.reserve(tableName.size() + fieldName.size() + 1);
531     ret.append(tableName).append(QLatin1Char('.')).append(fieldName);
532 
533     return ret;
534 }
535 
536 /*!
537     \reimp
538 */
selectStatement() const539 QString QSqlRelationalTableModel::selectStatement() const
540 {
541     Q_D(const QSqlRelationalTableModel);
542 
543     if (tableName().isEmpty())
544         return QString();
545     if (d->relations.isEmpty())
546         return QSqlTableModel::selectStatement();
547 
548     // Count how many times each field name occurs in the record
549     QHash<QString, int> fieldNames;
550     QStringList fieldList;
551     for (int i = 0; i < d->baseRec.count(); ++i) {
552         QSqlRelation relation = d->relations.value(i).rel;
553         QString name;
554         if (relation.isValid()) {
555             // Count the display column name, not the original foreign key
556             name = relation.displayColumn();
557             if (d->db.driver()->isIdentifierEscaped(name, QSqlDriver::FieldName))
558                 name = d->db.driver()->stripDelimiters(name, QSqlDriver::FieldName);
559 
560             const QSqlRecord rec = database().record(relation.tableName());
561             for (int i = 0; i < rec.count(); ++i) {
562                 if (name.compare(rec.fieldName(i), Qt::CaseInsensitive) == 0) {
563                     name = rec.fieldName(i);
564                     break;
565                 }
566             }
567         }
568         else {
569             name = d->baseRec.fieldName(i);
570         }
571         fieldNames[name] = fieldNames.value(name, 0) + 1;
572         fieldList.append(name);
573     }
574 
575     QString fList;
576     QString conditions;
577     QString from = Sql::from(tableName());
578     for (int i = 0; i < d->baseRec.count(); ++i) {
579         QSqlRelation relation = d->relations.value(i).rel;
580         const QString tableField = d->fullyQualifiedFieldName(tableName(), d->db.driver()->escapeIdentifier(d->baseRec.fieldName(i), QSqlDriver::FieldName));
581         if (relation.isValid()) {
582             const QString relTableAlias = Sql::relTablePrefix(i);
583             QString displayTableField = d->fullyQualifiedFieldName(relTableAlias, relation.displayColumn());
584 
585             // Duplicate field names must be aliased
586             if (fieldNames.value(fieldList[i]) > 1) {
587                 QString relTableName = relation.tableName().section(QChar::fromLatin1('.'), -1, -1);
588                 if (d->db.driver()->isIdentifierEscaped(relTableName, QSqlDriver::TableName))
589                     relTableName = d->db.driver()->stripDelimiters(relTableName, QSqlDriver::TableName);
590                 QString displayColumn = relation.displayColumn();
591                 if (d->db.driver()->isIdentifierEscaped(displayColumn, QSqlDriver::FieldName))
592                     displayColumn = d->db.driver()->stripDelimiters(displayColumn, QSqlDriver::FieldName);
593                 const QString alias = QString::fromLatin1("%1_%2_%3")
594                                       .arg(relTableName, displayColumn, QString::number(fieldNames.value(fieldList[i])));
595                 displayTableField = Sql::as(displayTableField, alias);
596                 --fieldNames[fieldList[i]];
597             }
598 
599             fList = Sql::comma(fList, displayTableField);
600 
601             // Join related table
602             const QString tblexpr = Sql::concat(relation.tableName(), relTableAlias);
603             const QString relTableField = d->fullyQualifiedFieldName(relTableAlias, relation.indexColumn());
604             const QString cond = Sql::eq(tableField, relTableField);
605             if (d->joinMode == QSqlRelationalTableModel::InnerJoin) {
606                 // FIXME: InnerJoin code is known to be broken.
607                 // Use LeftJoin mode if you want correct behavior.
608                 from = Sql::comma(from, tblexpr);
609                 conditions = Sql::et(conditions, cond);
610             } else {
611                 from = Sql::concat(from, Sql::leftJoin(tblexpr));
612                 from = Sql::concat(from, Sql::on(cond));
613             }
614         } else {
615             fList = Sql::comma(fList, tableField);
616         }
617     }
618 
619     if (fList.isEmpty())
620         return QString();
621 
622     const QString stmt = Sql::concat(Sql::select(fList), from);
623     const QString where = Sql::where(Sql::et(Sql::paren(conditions), Sql::paren(filter())));
624     return Sql::concat(Sql::concat(stmt, where), orderByClause());
625 }
626 
627 /*!
628     Returns a QSqlTableModel object for accessing the table for which
629     \a column is a foreign key, or \nullptr if there is no relation for
630     the given \a column.
631 
632     The returned object is owned by the QSqlRelationalTableModel.
633 
634     \sa setRelation(), relation()
635 */
relationModel(int column) const636 QSqlTableModel *QSqlRelationalTableModel::relationModel(int column) const
637 {
638     Q_D(const QSqlRelationalTableModel);
639     if (column < 0 || column >= d->relations.count())
640         return nullptr;
641 
642     QRelation &relation = const_cast<QSqlRelationalTableModelPrivate *>(d)->relations[column];
643     if (!relation.isValid())
644         return nullptr;
645 
646     if (!relation.model)
647         relation.populateModel();
648     return relation.model;
649 }
650 
651 /*!
652     \reimp
653 */
revertRow(int row)654 void QSqlRelationalTableModel::revertRow(int row)
655 {
656     QSqlTableModel::revertRow(row);
657 }
658 
659 /*!
660     \reimp
661 */
clear()662 void QSqlRelationalTableModel::clear()
663 {
664     Q_D(QSqlRelationalTableModel);
665     beginResetModel();
666     d->clearChanges();
667     d->relations.clear();
668     QSqlTableModel::clear();
669     endResetModel();
670 }
671 
672 
673 /*! \enum QSqlRelationalTableModel::JoinMode
674 
675     \value InnerJoin - Inner join mode, return rows when there is at least one match in both tables.
676     \value LeftJoin - Left join mode, returns all rows from the left table (table_name1), even if there are no matches in the right table (table_name2).
677 
678     \sa QSqlRelationalTableModel::setJoinMode()
679     \since 4.8
680 */
681 
682 /*!
683     Sets the SQL \a joinMode to show or hide rows with NULL foreign keys.
684     In InnerJoin mode (the default) these rows will not be shown: use the
685     LeftJoin mode if you want to show them.
686 
687     \sa QSqlRelationalTableModel::JoinMode
688     \since 4.8
689 */
setJoinMode(QSqlRelationalTableModel::JoinMode joinMode)690 void QSqlRelationalTableModel::setJoinMode( QSqlRelationalTableModel::JoinMode joinMode )
691 {
692     Q_D(QSqlRelationalTableModel);
693     d->joinMode = joinMode;
694 }
695 /*!
696     \reimp
697 */
select()698 bool QSqlRelationalTableModel::select()
699 {
700     return QSqlTableModel::select();
701 }
702 
703 /*!
704     \reimp
705 */
setTable(const QString & table)706 void QSqlRelationalTableModel::setTable(const QString &table)
707 {
708     Q_D(QSqlRelationalTableModel);
709 
710     // memorize the table before applying the relations
711     d->baseRec = d->db.record(table);
712 
713     QSqlTableModel::setTable(table);
714 }
715 
716 /*! \internal
717  */
translateFieldNames(QSqlRecord & values) const718 void QSqlRelationalTableModelPrivate::translateFieldNames(QSqlRecord &values) const
719 {
720     for (int i = 0; i < values.count(); ++i) {
721         if (relations.value(i).isValid()) {
722             QVariant v = values.value(i);
723             bool gen = values.isGenerated(i);
724             values.replace(i, baseRec.field(i));
725             values.setValue(i, v);
726             values.setGenerated(i, gen);
727         }
728     }
729 }
730 
731 /*!
732     \reimp
733 */
updateRowInTable(int row,const QSqlRecord & values)734 bool QSqlRelationalTableModel::updateRowInTable(int row, const QSqlRecord &values)
735 {
736     Q_D(QSqlRelationalTableModel);
737 
738     QSqlRecord rec = values;
739     d->translateFieldNames(rec);
740 
741     return QSqlTableModel::updateRowInTable(row, rec);
742 }
743 
744 /*!
745     \reimp
746 */
insertRowIntoTable(const QSqlRecord & values)747 bool QSqlRelationalTableModel::insertRowIntoTable(const QSqlRecord &values)
748 {
749     Q_D(QSqlRelationalTableModel);
750 
751     QSqlRecord rec = values;
752     d->translateFieldNames(rec);
753 
754     return QSqlTableModel::insertRowIntoTable(rec);
755 }
756 
757 /*!
758     \reimp
759 */
orderByClause() const760 QString QSqlRelationalTableModel::orderByClause() const
761 {
762     Q_D(const QSqlRelationalTableModel);
763 
764     const QSqlRelation rel = d->relations.value(d->sortColumn).rel;
765     if (!rel.isValid())
766         return QSqlTableModel::orderByClause();
767 
768     QString f = d->fullyQualifiedFieldName(Sql::relTablePrefix(d->sortColumn), rel.displayColumn());
769     f = d->sortOrder == Qt::AscendingOrder ? Sql::asc(f) : Sql::desc(f);
770     return Sql::orderBy(f);
771 }
772 
773 /*!
774     \reimp
775 */
removeColumns(int column,int count,const QModelIndex & parent)776 bool QSqlRelationalTableModel::removeColumns(int column, int count, const QModelIndex &parent)
777 {
778     Q_D(QSqlRelationalTableModel);
779 
780     if (parent.isValid() || column < 0 || column + count > d->rec.count())
781         return false;
782 
783     for (int i = 0; i < count; ++i) {
784         d->baseRec.remove(column);
785         if (d->relations.count() > column)
786             d->relations.remove(column);
787     }
788     return QSqlTableModel::removeColumns(column, count, parent);
789 }
790 
791 QT_END_NAMESPACE
792