Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsqlrelationaltablemodel.cpp
Go to the documentation of this file.
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
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
21
22using namespace Qt::StringLiterals;
23
25{
26public:
27 inline const static QString relTablePrefix(int i) { return QString::number(i).prepend("relTblAl_"_L1); }
28};
29
31
102
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() const;
112 void populateDictionary();
113 void clearDictionary();
114
115 void clear();
116 bool isValid() const;
117
120 QHash<QString, QVariant> dictionary;//maps keys to display values
121
122 private:
123 QSqlRelationalTableModel *m_parent;
124 bool m_dictInitialized;
125};
126
128{
129public:
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*/
142{
143 Q_ASSERT(parent != nullptr);
144 m_parent = parent;
145 rel = relation;
146}
147
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());
157 model->select();
158 }
159}
160
162{
163 return m_dictInitialized;
164}
165
167{
168 if (!isValid())
169 return;
170
171 if (model == nullptr)
173
175 QString indexColumn;
176 QString displayColumn;
177 for (int i=0; i < model->rowCount(); ++i) {
178 record = model->record(i);
179
180 indexColumn = rel.indexColumn();
181 if (m_parent->database().driver()->isIdentifierEscaped(indexColumn, QSqlDriver::FieldName))
182 indexColumn = m_parent->database().driver()->stripDelimiters(indexColumn, QSqlDriver::FieldName);
183
184 displayColumn = rel.displayColumn();
185 if (m_parent->database().driver()->isIdentifierEscaped(displayColumn, QSqlDriver::FieldName))
186 displayColumn = m_parent->database().driver()->stripDelimiters(displayColumn, QSqlDriver::FieldName);
187
188 dictionary[record.field(indexColumn).value().toString()] =
189 record.field(displayColumn).value();
190 }
191 m_dictInitialized = true;
192}
193
195{
197 m_dictInitialized = false;
198}
199
201{
202 delete model;
203 model = nullptr;
205}
206
208{
209 return (rel.isValid() && m_parent != nullptr);
210}
211
212
213
215 QSqlTableModel(parent, db), firstSelect(true), relation(rel)
216{
217}
218
220{
221 if (firstSelect) {
222 firstSelect = false;
223 return QSqlTableModel::select();
224 }
225 relation->clearDictionary();
227 if (res)
228 relation->populateDictionary();
229 return res;
230}
231
232
234{
235 Q_DECLARE_PUBLIC(QSqlRelationalTableModel)
236public:
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
252};
253
255{
256 for (auto &rel : relations)
257 rel.clear();
258}
259
264
266{
267 const QString fieldname = strippedFieldName(name);
268 int idx = baseRec.indexOf(fieldname);
269 if (idx == -1) {
270 // If the name is an alias we can find it here.
272 }
273 return idx;
274}
275
277{
278 for (auto &rel : relations)
279 rel.clearDictionary();
280
282}
283
381
388
393{
394 Q_D(const QSqlRelationalTableModel);
395
396 if (role == Qt::DisplayRole && index.column() >= 0 && index.column() < d->relations.size() &&
397 d->relations.value(index.column()).isValid()) {
398 QRelation &relation = d->relations[index.column()];
399 if (!relation.isDictionaryInitialized())
400 relation.populateDictionary();
401
402 //only perform a dictionary lookup for the display value
403 //when the value at index has been changed or added.
404 //At an unmodified index, the underlying model will
405 //already have the correct display value.
406 if (d->strategy != OnFieldChange) {
407 const QSqlTableModelPrivate::ModifiedRow row = d->cache.value(index.row());
408 if (row.op() != QSqlTableModelPrivate::None && row.rec().isGenerated(index.column())) {
409 if (d->strategy == OnManualSubmit || row.op() != QSqlTableModelPrivate::Delete) {
410 QVariant v = row.rec().value(index.column());
411 if (v.isValid())
412 return relation.dictionary[v.toString()];
413 }
414 }
415 }
416 }
417 return QSqlTableModel::data(index, role);
418}
419
436 int role)
437{
439 if ( role == Qt::EditRole && index.column() > 0 && index.column() < d->relations.size()
440 && d->relations.value(index.column()).isValid()) {
441 QRelation &relation = d->relations[index.column()];
442 if (!relation.isDictionaryInitialized())
443 relation.populateDictionary();
444 if (!relation.dictionary.contains(value.toString()))
445 return false;
446 }
447 return QSqlTableModel::setData(index, value, role);
448}
449
469{
471 if (column < 0)
472 return;
473 if (d->relations.size() <= column)
474 d->relations.resize(column + 1);
475 d->relations[column].init(this, relation);
476}
477
485{
486 Q_D(const QSqlRelationalTableModel);
487 return d->relations.value(column).rel;
488}
489
491 const QString &fieldName) const
492{
493 QString ret;
494 ret.reserve(tableName.size() + fieldName.size() + 1);
495 ret.append(tableName).append(u'.').append(fieldName);
496
497 return ret;
498}
499
504{
505 Q_D(const QSqlRelationalTableModel);
506
507 if (tableName().isEmpty())
508 return QString();
509 if (d->relations.isEmpty())
511
512 // Count how many times each field name occurs in the record
513 QHash<QString, int> fieldNames;
514 QStringList fieldList;
515 for (int i = 0; i < d->baseRec.count(); ++i) {
516 QSqlRelation relation = d->relations.value(i).rel;
518 if (relation.isValid()) {
519 // Count the display column name, not the original foreign key
521 if (d->db.driver()->isIdentifierEscaped(name, QSqlDriver::FieldName))
522 name = d->db.driver()->stripDelimiters(name, QSqlDriver::FieldName);
523
525 for (int i = 0; i < rec.count(); ++i) {
526 if (name.compare(rec.fieldName(i), Qt::CaseInsensitive) == 0) {
527 name = rec.fieldName(i);
528 break;
529 }
530 }
531 }
532 else {
533 name = d->baseRec.fieldName(i);
534 }
535 fieldNames[name] = fieldNames.value(name, 0) + 1;
536 fieldList.append(name);
537 }
538
539 QString fList;
540 QString conditions;
542 for (int i = 0; i < d->baseRec.count(); ++i) {
543 QSqlRelation relation = d->relations.value(i).rel;
544 const QString tableField = d->fullyQualifiedFieldName(tableName(), d->db.driver()->escapeIdentifier(d->baseRec.fieldName(i), QSqlDriver::FieldName));
545 if (relation.isValid()) {
546 const QString relTableAlias = SqlrTm::relTablePrefix(i);
547 QString displayTableField = d->fullyQualifiedFieldName(relTableAlias, relation.displayColumn());
548
549 // Duplicate field names must be aliased
550 if (fieldNames.value(fieldList[i]) > 1) {
551 QString relTableName = relation.tableName().section(QChar::fromLatin1('.'), -1, -1);
552 if (d->db.driver()->isIdentifierEscaped(relTableName, QSqlDriver::TableName))
553 relTableName = d->db.driver()->stripDelimiters(relTableName, QSqlDriver::TableName);
554 QString displayColumn = relation.displayColumn();
555 if (d->db.driver()->isIdentifierEscaped(displayColumn, QSqlDriver::FieldName))
556 displayColumn = d->db.driver()->stripDelimiters(displayColumn, QSqlDriver::FieldName);
557 QString alias = QString::fromLatin1("%1_%2_%3")
558 .arg(relTableName, displayColumn, QString::number(fieldNames.value(fieldList[i])));
559 alias.truncate(d->db.driver()->maximumIdentifierLength(QSqlDriver::FieldName));
560 alias = d->db.driver()->escapeIdentifier(alias, QSqlDriver::FieldName);
561 displayTableField = SqlrTm::as(displayTableField, alias);
562 --fieldNames[fieldList[i]];
563 }
564
565 fList = SqlrTm::comma(fList, displayTableField);
566
567 // Join related table
568 const QString tblexpr = SqlrTm::concat(relation.tableName(), relTableAlias);
569 const QString relTableField = d->fullyQualifiedFieldName(relTableAlias, relation.indexColumn());
570 const QString cond = SqlrTm::eq(tableField, relTableField);
571 if (d->joinMode == QSqlRelationalTableModel::InnerJoin) {
572 // FIXME: InnerJoin code is known to be broken.
573 // Use LeftJoin mode if you want correct behavior.
574 from = SqlrTm::comma(from, tblexpr);
575 conditions = SqlrTm::et(conditions, cond);
576 } else {
577 from = SqlrTm::concat(from, SqlrTm::leftJoin(tblexpr));
578 from = SqlrTm::concat(from, SqlrTm::on(cond));
579 }
580 } else {
581 fList = SqlrTm::comma(fList, tableField);
582 }
583 }
584
585 if (fList.isEmpty())
586 return QString();
587
588 const QString stmt = SqlrTm::concat(SqlrTm::select(fList), from);
589 const QString where = SqlrTm::where(SqlrTm::et(SqlrTm::paren(conditions), SqlrTm::paren(filter())));
590 return SqlrTm::concat(SqlrTm::concat(stmt, where), orderByClause());
591}
592
603{
604 Q_D(const QSqlRelationalTableModel);
605 if (column < 0 || column >= d->relations.size())
606 return nullptr;
607
608 QRelation &relation = const_cast<QSqlRelationalTableModelPrivate *>(d)->relations[column];
609 if (!relation.isValid())
610 return nullptr;
611
612 if (!relation.model)
613 relation.populateModel();
614 return relation.model;
615}
616
624
629{
632 d->clearChanges();
633 d->relations.clear();
636}
637
638
655{
657 d->joinMode = joinMode;
658}
666
671{
673
674 // memorize the table before applying the relations
675 d->baseRec = d->db.record(table);
676
678}
679
683{
684 for (int i = 0; i < values.count(); ++i) {
685 if (relations.value(i).isValid()) {
687 bool gen = values.isGenerated(i);
688 values.replace(i, baseRec.field(i));
689 values.setValue(i, v);
690 values.setGenerated(i, gen);
691 }
692 }
693}
694
699{
701
702 QSqlRecord rec = values;
703 d->translateFieldNames(rec);
704
706}
707
712{
714
715 QSqlRecord rec = values;
716 d->translateFieldNames(rec);
717
719}
720
725{
726 Q_D(const QSqlRelationalTableModel);
727
728 const QSqlRelation rel = d->relations.value(d->sortColumn).rel;
729 if (!rel.isValid())
731
732 QString f = d->fullyQualifiedFieldName(SqlrTm::relTablePrefix(d->sortColumn), rel.displayColumn());
733 f = d->sortOrder == Qt::AscendingOrder ? SqlrTm::asc(f) : SqlrTm::desc(f);
734 return SqlrTm::orderBy(f);
735}
736
741{
743
744 if (parent.isValid() || column < 0 || column + count > d->rec.count())
745 return false;
746
747 for (int i = 0; i < count; ++i) {
748 d->baseRec.remove(column);
749 if (d->relations.size() > column)
750 d->relations.remove(column);
751 }
753}
754
756
757#include "moc_qsqlrelationaltablemodel.cpp"
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:952
T value(qsizetype i) const
Definition qlist.h:665
\inmodule QtCore
\inmodule QtCore
Definition qobject.h:103
QRelatedTableModel(QRelation *rel, QObject *parent=nullptr, const QSqlDatabase &db=QSqlDatabase())
bool select() override
Populates the model with data from the table that was set via setTable(), using the specified filter ...
The QSqlDatabase class handles a connection to a database.
QSqlDriver * driver() const
Returns the database driver used to access the database connection.
QSqlRecord record(const QString &tablename) const
Returns a QSqlRecord populated with the names of all the fields in the table (or view) called tablena...
virtual QString stripDelimiters(const QString &identifier, IdentifierType type) const
Returns the identifier with the leading and trailing delimiters removed, identifier can either be a t...
virtual bool isIdentifierEscaped(const QString &identifier, IdentifierType type) const
Returns whether identifier is escaped according to the database rules.
static const QLatin1StringView et()
static const QLatin1StringView orderBy()
static const QLatin1StringView asc()
static const QLatin1StringView comma()
static const QLatin1StringView as()
static const QLatin1StringView leftJoin()
static const QLatin1StringView desc()
static const QLatin1StringView select()
static const QLatin1StringView on()
static const QString paren(const QString &s)
static const QString concat(const QString &a, const QString &b)
static const QLatin1StringView from()
static const QLatin1StringView where()
static const QLatin1StringView eq()
The QSqlRecord class encapsulates a database record.
Definition qsqlrecord.h:20
QSqlField field(int i) const
Returns the field at position index.
int count() const
Returns the number of fields in the record.
int indexOf(QAnyStringView name) const
Returns the position of the field called name within the record, or -1 if it cannot be found.
QString fieldName(int i) const
Returns the name of the field at position index.
QString tableName() const
Returns the name of the table to which a foreign key refers.
bool isValid() const noexcept
Returns true if the QSqlRelation object is valid; otherwise returns false.
QString indexColumn() const
Returns the index column from table tableName() to which a foreign key refers.
QString displayColumn() const
Returns the column from table tableName() that should be presented to the user instead of a foreign k...
int nameToIndex(const QString &name) const override
QString fullyQualifiedFieldName(const QString &tableName, const QString &fieldName) const
void translateFieldNames(QSqlRecord &values) const
QSqlRelationalTableModel::JoinMode joinMode
static const QString relTablePrefix(int i)
The QSqlRelationalTableModel class provides an editable data model for a single database table,...
virtual void setRelation(int column, const QSqlRelation &relation)
Lets the specified column be a foreign index specified by relation.
QSqlRelation relation(int column) const
Returns the relation for the column column, or an invalid relation if no relation is set.
JoinMode
\value InnerJoin - Inner join mode, return rows when there is at least one match in both tables.
QVariant data(const QModelIndex &item, int role=Qt::DisplayRole) const override
\reimp
QString selectStatement() const override
\reimp
bool removeColumns(int column, int count, const QModelIndex &parent=QModelIndex()) override
\reimp
QString orderByClause() const override
\reimp
bool updateRowInTable(int row, const QSqlRecord &values) override
\reimp
void setTable(const QString &tableName) override
\reimp
void revertRow(int row) override
\reimp
virtual QSqlTableModel * relationModel(int column) const
Returns a QSqlTableModel object for accessing the table for which column is a foreign key,...
bool setData(const QModelIndex &item, const QVariant &value, int role=Qt::EditRole) override
Sets the data for the role in the item with the specified index to the value given.
void setJoinMode(QSqlRelationalTableModel::JoinMode joinMode)
Sets the SQL joinMode to show or hide rows with NULL foreign keys.
bool insertRowIntoTable(const QSqlRecord &values) override
\reimp
virtual ~QSqlRelationalTableModel()
Destroys the object and frees any allocated resources.
QSqlRelationalTableModel(QObject *parent=nullptr, const QSqlDatabase &db=QSqlDatabase())
Creates an empty QSqlRelationalTableModel and sets the parent to parent and the database connection t...
virtual int nameToIndex(const QString &name) const
virtual void clearCache()
virtual void revertCachedRow(int row)
QString strippedFieldName(const QString &name) const
The QSqlTableModel class provides an editable data model for a single database table.
virtual QString orderByClause() const
Returns an SQL {ORDER BY} clause based on the currently set sort order.
bool removeColumns(int column, int count, const QModelIndex &parent=QModelIndex()) override
Removes count columns from the parent model, starting at index column.
QVariant data(const QModelIndex &idx, int role=Qt::DisplayRole) const override
\reimp
QSqlRecord record() const
This is an overloaded function.
QString filter() const
Returns the currently set filter.
virtual bool updateRowInTable(int row, const QSqlRecord &values)
Updates the given row in the currently active database table with the specified values.
virtual bool insertRowIntoTable(const QSqlRecord &values)
Inserts the values values into the currently active database table.
void clear() override
\reimp
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Sets the data for the item index for the role role to value.
QString tableName() const
Returns the name of the currently selected table.
virtual void setTable(const QString &tableName)
Sets the database table on which the model operates to tableName.
virtual QString selectStatement() const
Returns the SQL SELECT statement used internally to populate the model.
virtual bool select()
Populates the model with data from the table that was set via setTable(), using the specified filter ...
QSqlDatabase database() const
Returns the model's database connection.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
\reimp
virtual void revertRow(int row)
Reverts all changes for the specified row.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
void reserve(qsizetype size)
Ensures the string has space for at least size characters.
Definition qstring.h:1325
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5881
void truncate(qsizetype pos)
Truncates the string starting from, and including, the element at index position.
Definition qstring.cpp:6330
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
QString section(QChar sep, qsizetype start, qsizetype end=-1, SectionFlags flags=SectionDefault) const
This function returns a section of the string.
Definition qstring.h:1284
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8095
QString & append(QChar c)
Definition qstring.cpp:3254
\inmodule QtCore
Definition qvariant.h:65
T value() const &
Definition qvariant.h:517
Combined button and popup list for selecting options.
@ EditRole
@ DisplayRole
@ AscendingOrder
Definition qnamespace.h:122
@ CaseInsensitive
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
return ret
GLenum GLsizei GLsizei GLint * values
[15]
GLsizei const GLfloat * v
[13]
GLuint index
[2]
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLuint name
GLenum GLenum GLsizei void GLsizei void * column
GLuint res
GLenum GLenum GLsizei void * row
GLenum GLenum GLsizei void * table
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QObject::connect nullptr
QMimeDatabase db
[0]
MyRecord record(int row) const
[0]
QRelatedTableModel * model
void init(QSqlRelationalTableModel *parent, const QSqlRelation &relation)
bool isDictionaryInitialized() const
QHash< QString, QVariant > dictionary