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

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