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
56QT_BEGIN_NAMESPACE
57
58class QSqlRelationalTableModelSql: public QSqlTableModelSql
59{
60public:
61 inline const static QString relTablePrefix(int i) { return QString::number(i).prepend(s: QLatin1String("relTblAl_")); }
62};
63
64typedef 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
135class QRelatedTableModel;
136
137struct QRelation
138{
139 public:
140 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
161class QRelatedTableModel : public QSqlTableModel
162{
163public:
164 QRelatedTableModel(QRelation *rel, QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase());
165 bool select() override;
166private:
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*/
175void QRelation::init(QSqlRelationalTableModel *parent, const QSqlRelation &relation)
176{
177 Q_ASSERT(parent != nullptr);
178 m_parent = parent;
179 rel = relation;
180}
181
182void 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
195bool QRelation::isDictionaryInitialized()
196{
197 return m_dictInitialized;
198}
199
200void 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(row: i);
213
214 indexColumn = rel.indexColumn();
215 if (m_parent->database().driver()->isIdentifierEscaped(identifier: indexColumn, type: QSqlDriver::FieldName))
216 indexColumn = m_parent->database().driver()->stripDelimiters(identifier: indexColumn, type: QSqlDriver::FieldName);
217
218 displayColumn = rel.displayColumn();
219 if (m_parent->database().driver()->isIdentifierEscaped(identifier: displayColumn, type: QSqlDriver::FieldName))
220 displayColumn = m_parent->database().driver()->stripDelimiters(identifier: displayColumn, type: QSqlDriver::FieldName);
221
222 dictionary[record.field(name: indexColumn).value().toString()] =
223 record.field(name: displayColumn).value();
224 }
225 m_dictInitialized = true;
226}
227
228void QRelation::clearDictionary()
229{
230 dictionary.clear();
231 m_dictInitialized = false;
232}
233
234void QRelation::clear()
235{
236 delete model;
237 model = nullptr;
238 clearDictionary();
239}
240
241bool QRelation::isValid()
242{
243 return (rel.isValid() && m_parent != nullptr);
244}
245
246
247
248QRelatedTableModel::QRelatedTableModel(QRelation *rel, QObject *parent, QSqlDatabase db) :
249 QSqlTableModel(parent, db), firstSelect(true), relation(rel)
250{
251}
252
253bool 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
267class QSqlRelationalTableModelPrivate: public QSqlTableModelPrivate
268{
269 Q_DECLARE_PUBLIC(QSqlRelationalTableModel)
270public:
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
288void QSqlRelationalTableModelPrivate::clearChanges()
289{
290 for (int i = 0; i < relations.count(); ++i) {
291 QRelation &rel = relations[i];
292 rel.clear();
293 }
294}
295
296void QSqlRelationalTableModelPrivate::revertCachedRow(int row)
297{
298 QSqlTableModelPrivate::revertCachedRow(row);
299}
300
301int QSqlRelationalTableModelPrivate::nameToIndex(const QString &name) const
302{
303 const QString fieldname = strippedFieldName(name);
304 int idx = baseRec.indexOf(name: 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
312void 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*/
413QSqlRelationalTableModel::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*/
421QSqlRelationalTableModel::~QSqlRelationalTableModel()
422{
423}
424
425/*!
426 \reimp
427*/
428QVariant 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(i: 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(akey: index.row());
444 if (row.op() != QSqlTableModelPrivate::None && row.rec().isGenerated(i: index.column())) {
445 if (d->strategy == OnManualSubmit || row.op() != QSqlTableModelPrivate::Delete) {
446 QVariant v = row.rec().value(i: index.column());
447 if (v.isValid())
448 return relation.dictionary[v.toString()];
449 }
450 }
451 }
452 }
453 return QSqlTableModel::data(idx: 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*/
471bool 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(i: index.column()).isValid()) {
477 QRelation &relation = d->relations[index.column()];
478 if (!relation.isDictionaryInitialized())
479 relation.populateDictionary();
480 if (!relation.dictionary.contains(akey: 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*/
504void 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(asize: column + 1);
511 d->relations[column].init(parent: 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*/
520QSqlRelation QSqlRelationalTableModel::relation(int column) const
521{
522 Q_D(const QSqlRelationalTableModel);
523 return d->relations.value(i: column).rel;
524}
525
526QString QSqlRelationalTableModelPrivate::fullyQualifiedFieldName(const QString &tableName,
527 const QString &fieldName) const
528{
529 QString ret;
530 ret.reserve(asize: tableName.size() + fieldName.size() + 1);
531 ret.append(s: tableName).append(c: QLatin1Char('.')).append(s: fieldName);
532
533 return ret;
534}
535
536/*!
537 \reimp
538*/
539QString 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(identifier: name, type: QSqlDriver::FieldName))
558 name = d->db.driver()->stripDelimiters(identifier: name, type: QSqlDriver::FieldName);
559
560 const QSqlRecord rec = database().record(tablename: relation.tableName());
561 for (int i = 0; i < rec.count(); ++i) {
562 if (name.compare(s: rec.fieldName(i), cs: 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(akey: name, adefaultValue: 0) + 1;
572 fieldList.append(t: name);
573 }
574
575 QString fList;
576 QString conditions;
577 QString from = Sql::from(s: 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: tableName(), fieldName: d->db.driver()->escapeIdentifier(identifier: d->baseRec.fieldName(i), type: QSqlDriver::FieldName));
581 if (relation.isValid()) {
582 const QString relTableAlias = Sql::relTablePrefix(i);
583 QString displayTableField = d->fullyQualifiedFieldName(tableName: relTableAlias, fieldName: relation.displayColumn());
584
585 // Duplicate field names must be aliased
586 if (fieldNames.value(akey: fieldList[i]) > 1) {
587 QString relTableName = relation.tableName().section(asep: QChar::fromLatin1(c: '.'), astart: -1, aend: -1);
588 if (d->db.driver()->isIdentifierEscaped(identifier: relTableName, type: QSqlDriver::TableName))
589 relTableName = d->db.driver()->stripDelimiters(identifier: relTableName, type: QSqlDriver::TableName);
590 QString displayColumn = relation.displayColumn();
591 if (d->db.driver()->isIdentifierEscaped(identifier: displayColumn, type: QSqlDriver::FieldName))
592 displayColumn = d->db.driver()->stripDelimiters(identifier: displayColumn, type: QSqlDriver::FieldName);
593 const QString alias = QString::fromLatin1(str: "%1_%2_%3")
594 .arg(args&: relTableName, args&: displayColumn, args: QString::number(fieldNames.value(akey: fieldList[i])));
595 displayTableField = Sql::as(a: displayTableField, b: alias);
596 --fieldNames[fieldList[i]];
597 }
598
599 fList = Sql::comma(a: fList, b: displayTableField);
600
601 // Join related table
602 const QString tblexpr = Sql::concat(a: relation.tableName(), b: relTableAlias);
603 const QString relTableField = d->fullyQualifiedFieldName(tableName: relTableAlias, fieldName: relation.indexColumn());
604 const QString cond = Sql::eq(a: tableField, b: 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(a: from, b: tblexpr);
609 conditions = Sql::et(a: conditions, b: cond);
610 } else {
611 from = Sql::concat(a: from, b: Sql::leftJoin(s: tblexpr));
612 from = Sql::concat(a: from, b: Sql::on(s: cond));
613 }
614 } else {
615 fList = Sql::comma(a: fList, b: tableField);
616 }
617 }
618
619 if (fList.isEmpty())
620 return QString();
621
622 const QString stmt = Sql::concat(a: Sql::select(s: fList), b: from);
623 const QString where = Sql::where(s: Sql::et(a: Sql::paren(s: conditions), b: Sql::paren(s: filter())));
624 return Sql::concat(a: Sql::concat(a: stmt, b: where), b: 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*/
636QSqlTableModel *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*/
654void QSqlRelationalTableModel::revertRow(int row)
655{
656 QSqlTableModel::revertRow(row);
657}
658
659/*!
660 \reimp
661*/
662void 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*/
690void QSqlRelationalTableModel::setJoinMode( QSqlRelationalTableModel::JoinMode joinMode )
691{
692 Q_D(QSqlRelationalTableModel);
693 d->joinMode = joinMode;
694}
695/*!
696 \reimp
697*/
698bool QSqlRelationalTableModel::select()
699{
700 return QSqlTableModel::select();
701}
702
703/*!
704 \reimp
705*/
706void 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(tablename: table);
712
713 QSqlTableModel::setTable(table);
714}
715
716/*! \internal
717 */
718void 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(pos: i, field: baseRec.field(i));
725 values.setValue(i, val: v);
726 values.setGenerated(i, generated: gen);
727 }
728 }
729}
730
731/*!
732 \reimp
733*/
734bool QSqlRelationalTableModel::updateRowInTable(int row, const QSqlRecord &values)
735{
736 Q_D(QSqlRelationalTableModel);
737
738 QSqlRecord rec = values;
739 d->translateFieldNames(values&: rec);
740
741 return QSqlTableModel::updateRowInTable(row, values: rec);
742}
743
744/*!
745 \reimp
746*/
747bool QSqlRelationalTableModel::insertRowIntoTable(const QSqlRecord &values)
748{
749 Q_D(QSqlRelationalTableModel);
750
751 QSqlRecord rec = values;
752 d->translateFieldNames(values&: rec);
753
754 return QSqlTableModel::insertRowIntoTable(values: rec);
755}
756
757/*!
758 \reimp
759*/
760QString QSqlRelationalTableModel::orderByClause() const
761{
762 Q_D(const QSqlRelationalTableModel);
763
764 const QSqlRelation rel = d->relations.value(i: d->sortColumn).rel;
765 if (!rel.isValid())
766 return QSqlTableModel::orderByClause();
767
768 QString f = d->fullyQualifiedFieldName(tableName: Sql::relTablePrefix(i: d->sortColumn), fieldName: rel.displayColumn());
769 f = d->sortOrder == Qt::AscendingOrder ? Sql::asc(s: f) : Sql::desc(s: f);
770 return Sql::orderBy(s: f);
771}
772
773/*!
774 \reimp
775*/
776bool 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(pos: column);
785 if (d->relations.count() > column)
786 d->relations.remove(i: column);
787 }
788 return QSqlTableModel::removeColumns(column, count, parent);
789}
790
791QT_END_NAMESPACE
792

source code of qtbase/src/sql/models/qsqlrelationaltablemodel.cpp