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
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. The index must also exist in the referenced
446 table, otherwise the function returns \c false.
447
448 \sa editStrategy(), data(), submit(), revertRow()
449*/
450bool QSqlRelationalTableModel::setData(const QModelIndex &index, const QVariant &value,
451 int role)
452{
453 Q_D(QSqlRelationalTableModel);
454 if ( role == Qt::EditRole && index.column() > 0 && index.column() < d->relations.size()
455 && d->relations.at(i: index.column())->isValid()) {
456 auto relation = d->relations.at(i: index.column());
457 if (!relation->isDictionaryInitialized())
458 relation->populateDictionary();
459 if (!relation->dictionary.contains(key: value.toString()))
460 return false;
461 }
462 return QSqlTableModel::setData(index, value, role);
463}
464
465/*!
466 Lets the specified \a column be a foreign index specified by \a relation.
467
468 Example:
469
470 \snippet relationaltablemodel/relationaltablemodel.cpp 0
471 \codeline
472 \snippet relationaltablemodel/relationaltablemodel.cpp 1
473
474 The setRelation() call specifies that column 2 in table \c
475 employee is a foreign key that maps with field \c id of table \c
476 city, and that the view should present the \c{city}'s \c name
477 field to the user.
478
479 Note: The table's primary key may not contain a relation to another table.
480
481 \sa relation()
482*/
483void QSqlRelationalTableModel::setRelation(int column, const QSqlRelation &relation)
484{
485 Q_D(QSqlRelationalTableModel);
486 if (column < 0)
487 return;
488 if (d->relations.size() <= column) {
489 const auto oldSize = d->relations.size();
490 d->relations.resize(size: column + 1);
491 for (auto i = oldSize; i < d->relations.size(); ++i)
492 d->relations[i] = QSharedPointer<QRelation>::create();
493 }
494 d->relations.at(i: column)->init(parent: this, relation, column);
495}
496
497/*!
498 Returns the relation for the column \a column, or an invalid
499 relation if no relation is set.
500
501 \sa setRelation(), QSqlRelation::isValid()
502*/
503QSqlRelation QSqlRelationalTableModel::relation(int column) const
504{
505 Q_D(const QSqlRelationalTableModel);
506 return d->relations.value(i: column) ? d->relations.at(i: column)->rel : QSqlRelation();
507}
508
509QString QSqlRelationalTableModelPrivate::fullyQualifiedFieldName(const QString &tableName,
510 const QString &fieldName) const
511{
512 QString ret;
513 ret.reserve(asize: tableName.size() + fieldName.size() + 1);
514 ret.append(s: tableName).append(c: u'.').append(s: fieldName);
515
516 return ret;
517}
518
519/*!
520 \reimp
521*/
522QString QSqlRelationalTableModel::selectStatement() const
523{
524 Q_D(const QSqlRelationalTableModel);
525
526 if (tableName().isEmpty())
527 return QString();
528 if (d->relations.isEmpty())
529 return QSqlTableModel::selectStatement();
530
531 // Count how many times each field name occurs in the record
532 QHash<QString, int> fieldNames;
533 QStringList fieldList;
534 for (int i = 0; i < d->baseRec.count(); ++i) {
535 QSqlRelation relation = d->relations.value(i) ? d->relations.at(i)->rel : QSqlRelation();
536 QString name;
537 if (relation.isValid()) {
538 // Count the display column name, not the original foreign key
539 name = relation.displayColumn();
540 if (d->db.driver()->isIdentifierEscaped(identifier: name, type: QSqlDriver::FieldName))
541 name = d->db.driver()->stripDelimiters(identifier: name, type: QSqlDriver::FieldName);
542
543 const QSqlRecord rec = database().record(tablename: relation.tableName());
544 for (int i = 0; i < rec.count(); ++i) {
545 if (name.compare(s: rec.fieldName(i), cs: Qt::CaseInsensitive) == 0) {
546 name = rec.fieldName(i);
547 break;
548 }
549 }
550 }
551 else {
552 name = d->baseRec.fieldName(i);
553 }
554 fieldNames[name] = fieldNames.value(key: name, defaultValue: 0) + 1;
555 fieldList.append(t: name);
556 }
557
558 QString fList;
559 QString conditions;
560 QString from = SqlrTm::from(s: tableName());
561 for (int i = 0; i < d->baseRec.count(); ++i) {
562 QSqlRelation relation = d->relations.value(i) ? d->relations.at(i)->rel : QSqlRelation();
563 const QString tableField = d->fullyQualifiedFieldName(tableName: tableName(), fieldName: d->db.driver()->escapeIdentifier(identifier: d->baseRec.fieldName(i), type: QSqlDriver::FieldName));
564 if (relation.isValid()) {
565 const QString relTableAlias = SqlrTm::relTablePrefix(i);
566 QString displayTableField = d->fullyQualifiedFieldName(tableName: relTableAlias, fieldName: relation.displayColumn());
567
568 // Duplicate field names must be aliased
569 if (fieldNames.value(key: fieldList[i]) > 1) {
570 QString relTableName = relation.tableName().section(asep: QChar::fromLatin1(c: '.'), astart: -1, aend: -1);
571 if (d->db.driver()->isIdentifierEscaped(identifier: relTableName, type: QSqlDriver::TableName))
572 relTableName = d->db.driver()->stripDelimiters(identifier: relTableName, type: QSqlDriver::TableName);
573 QString displayColumn = relation.displayColumn();
574 if (d->db.driver()->isIdentifierEscaped(identifier: displayColumn, type: QSqlDriver::FieldName))
575 displayColumn = d->db.driver()->stripDelimiters(identifier: displayColumn, type: QSqlDriver::FieldName);
576 QString alias = QString::fromLatin1(ba: "%1_%2_%3")
577 .arg(args&: relTableName, args&: displayColumn, args: QString::number(fieldNames.value(key: fieldList[i])));
578 alias.truncate(pos: d->db.driver()->maximumIdentifierLength(type: QSqlDriver::FieldName));
579 alias = d->db.driver()->escapeIdentifier(identifier: alias, type: QSqlDriver::FieldName);
580 displayTableField = SqlrTm::as(a: displayTableField, b: alias);
581 --fieldNames[fieldList[i]];
582 }
583
584 fList = SqlrTm::comma(a: fList, b: displayTableField);
585
586 // Join related table
587 const QString tblexpr = SqlrTm::concat(a: relation.tableName(), b: relTableAlias);
588 const QString relTableField = d->fullyQualifiedFieldName(tableName: relTableAlias, fieldName: relation.indexColumn());
589 const QString cond = SqlrTm::eq(a: tableField, b: relTableField);
590 if (d->joinMode == QSqlRelationalTableModel::InnerJoin) {
591 // FIXME: InnerJoin code is known to be broken.
592 // Use LeftJoin mode if you want correct behavior.
593 from = SqlrTm::comma(a: from, b: tblexpr);
594 conditions = SqlrTm::et(a: conditions, b: cond);
595 } else {
596 from = SqlrTm::concat(a: from, b: SqlrTm::leftJoin(s: tblexpr));
597 from = SqlrTm::concat(a: from, b: SqlrTm::on(s: cond));
598 }
599 } else {
600 fList = SqlrTm::comma(a: fList, b: tableField);
601 }
602 }
603
604 if (fList.isEmpty())
605 return QString();
606
607 const QString stmt = SqlrTm::concat(a: SqlrTm::select(s: fList), b: from);
608 const QString where = SqlrTm::where(s: SqlrTm::et(a: SqlrTm::paren(s: conditions), b: SqlrTm::paren(s: filter())));
609 return SqlrTm::concat(a: SqlrTm::concat(a: stmt, b: where), b: orderByClause());
610}
611
612/*!
613 Returns a QSqlTableModel object for accessing the table for which
614 \a column is a foreign key, or \nullptr if there is no relation for
615 the given \a column.
616
617 The returned object is owned by the QSqlRelationalTableModel.
618
619 \sa setRelation(), relation()
620*/
621QSqlTableModel *QSqlRelationalTableModel::relationModel(int column) const
622{
623 Q_D(const QSqlRelationalTableModel);
624 if (column < 0 || column >= d->relations.size())
625 return nullptr;
626
627 auto relation = d->relations.at(i: column);
628 if (!relation || !relation->isValid())
629 return nullptr;
630
631 if (!relation->model)
632 relation->populateModel();
633 return relation->model;
634}
635
636/*!
637 \reimp
638*/
639void QSqlRelationalTableModel::revertRow(int row)
640{
641 QSqlTableModel::revertRow(row);
642}
643
644/*!
645 \reimp
646*/
647void QSqlRelationalTableModel::clear()
648{
649 Q_D(QSqlRelationalTableModel);
650 beginResetModel();
651 d->clearChanges();
652 d->relations.clear();
653 QSqlTableModel::clear();
654 endResetModel();
655}
656
657
658/*! \enum QSqlRelationalTableModel::JoinMode
659
660 \value InnerJoin - Inner join mode, return rows when there is at least one match in both tables.
661 \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).
662
663 \sa QSqlRelationalTableModel::setJoinMode()
664*/
665
666/*!
667 Sets the SQL \a joinMode to show or hide rows with NULL foreign keys.
668 In InnerJoin mode (the default) these rows will not be shown: use the
669 LeftJoin mode if you want to show them.
670
671 \sa QSqlRelationalTableModel::JoinMode
672*/
673void QSqlRelationalTableModel::setJoinMode( QSqlRelationalTableModel::JoinMode joinMode )
674{
675 Q_D(QSqlRelationalTableModel);
676 d->joinMode = joinMode;
677}
678/*!
679 \reimp
680*/
681bool QSqlRelationalTableModel::select()
682{
683 return QSqlTableModel::select();
684}
685
686/*!
687 \reimp
688*/
689void QSqlRelationalTableModel::setTable(const QString &table)
690{
691 Q_D(QSqlRelationalTableModel);
692
693 // memorize the table before applying the relations
694 d->baseRec = d->db.record(tablename: table);
695
696 QSqlTableModel::setTable(table);
697}
698
699/*! \internal
700 */
701void QSqlRelationalTableModelPrivate::translateFieldNames(QSqlRecord &values) const
702{
703 for (int i = 0; i < values.count(); ++i) {
704 if (relations.value(i) && relations.at(i)->isValid()) {
705 QVariant v = values.value(i);
706 bool gen = values.isGenerated(i);
707 values.replace(pos: i, field: baseRec.field(i));
708 values.setValue(i, val: v);
709 values.setGenerated(i, generated: gen);
710 }
711 }
712}
713
714/*!
715 \reimp
716*/
717bool QSqlRelationalTableModel::updateRowInTable(int row, const QSqlRecord &values)
718{
719 Q_D(QSqlRelationalTableModel);
720
721 QSqlRecord rec = values;
722 d->translateFieldNames(values&: rec);
723
724 return QSqlTableModel::updateRowInTable(row, values: rec);
725}
726
727/*!
728 \reimp
729*/
730bool QSqlRelationalTableModel::insertRowIntoTable(const QSqlRecord &values)
731{
732 Q_D(QSqlRelationalTableModel);
733
734 QSqlRecord rec = values;
735 d->translateFieldNames(values&: rec);
736
737 return QSqlTableModel::insertRowIntoTable(values: rec);
738}
739
740/*!
741 \reimp
742*/
743QString QSqlRelationalTableModel::orderByClause() const
744{
745 Q_D(const QSqlRelationalTableModel);
746
747 const QSqlRelation rel = d->relations.value(i: d->sortColumn) ? d->relations.at(i: d->sortColumn)->rel : QSqlRelation();
748 if (!rel.isValid())
749 return QSqlTableModel::orderByClause();
750
751 QString f = d->fullyQualifiedFieldName(tableName: SqlrTm::relTablePrefix(i: d->sortColumn), fieldName: rel.displayColumn());
752 f = d->sortOrder == Qt::AscendingOrder ? SqlrTm::asc(s: f) : SqlrTm::desc(s: f);
753 return SqlrTm::orderBy(s: f);
754}
755
756/*!
757 \reimp
758*/
759bool QSqlRelationalTableModel::removeColumns(int column, int count, const QModelIndex &parent)
760{
761 Q_D(QSqlRelationalTableModel);
762
763 if (parent.isValid() || column < 0 || column + count > d->rec.count())
764 return false;
765
766 for (int i = 0; i < count; ++i) {
767 d->baseRec.remove(pos: column);
768 if (d->relations.size() > column)
769 d->relations.remove(i: column);
770 }
771 return QSqlTableModel::removeColumns(column, count, parent);
772}
773
774QT_END_NAMESPACE
775
776#include "moc_qsqlrelationaltablemodel.cpp"
777

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