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

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