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
112 void populateDictionary();
113 void clearDictionary();
114
115 void clear();
116 bool isValid();
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 (int i = 0; i < relations.size(); ++i) {
257 QRelation &rel = relations[i];
258 rel.clear();
259 }
260}
261
266
268{
269 const QString fieldname = strippedFieldName(name);
270 int idx = baseRec.indexOf(fieldname);
271 if (idx == -1) {
272 // If the name is an alias we can find it here.
274 }
275 return idx;
276}
277
279{
280 for (int i = 0; i < relations.size(); ++i)
281 relations[i].clearDictionary();
282
284}
285
383
390
395{
396 Q_D(const QSqlRelationalTableModel);
397
398 if (role == Qt::DisplayRole && index.column() >= 0 && index.column() < d->relations.size() &&
399 d->relations.value(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(index.row());
410 if (row.op() != QSqlTableModelPrivate::None && row.rec().isGenerated(index.column())) {
411 if (d->strategy == OnManualSubmit || row.op() != QSqlTableModelPrivate::Delete) {
412 QVariant v = row.rec().value(index.column());
413 if (v.isValid())
414 return relation.dictionary[v.toString()];
415 }
416 }
417 }
418 }
419 return QSqlTableModel::data(index, role);
420}
421
438 int role)
439{
441 if ( role == Qt::EditRole && index.column() > 0 && index.column() < d->relations.size()
442 && d->relations.value(index.column()).isValid()) {
443 QRelation &relation = d->relations[index.column()];
444 if (!relation.isDictionaryInitialized())
445 relation.populateDictionary();
446 if (!relation.dictionary.contains(value.toString()))
447 return false;
448 }
449 return QSqlTableModel::setData(index, value, role);
450}
451
471{
473 if (column < 0)
474 return;
475 if (d->relations.size() <= column)
476 d->relations.resize(column + 1);
477 d->relations[column].init(this, relation);
478}
479
487{
488 Q_D(const QSqlRelationalTableModel);
489 return d->relations.value(column).rel;
490}
491
493 const QString &fieldName) const
494{
495 QString ret;
496 ret.reserve(tableName.size() + fieldName.size() + 1);
497 ret.append(tableName).append(u'.').append(fieldName);
498
499 return ret;
500}
501
506{
507 Q_D(const QSqlRelationalTableModel);
508
509 if (tableName().isEmpty())
510 return QString();
511 if (d->relations.isEmpty())
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;
520 if (relation.isValid()) {
521 // Count the display column name, not the original foreign key
523 if (d->db.driver()->isIdentifierEscaped(name, QSqlDriver::FieldName))
524 name = d->db.driver()->stripDelimiters(name, QSqlDriver::FieldName);
525
527 for (int i = 0; i < rec.count(); ++i) {
528 if (name.compare(rec.fieldName(i), 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(name, 0) + 1;
538 fieldList.append(name);
539 }
540
541 QString fList;
542 QString conditions;
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(), d->db.driver()->escapeIdentifier(d->baseRec.fieldName(i), QSqlDriver::FieldName));
547 if (relation.isValid()) {
548 const QString relTableAlias = SqlrTm::relTablePrefix(i);
549 QString displayTableField = d->fullyQualifiedFieldName(relTableAlias, relation.displayColumn());
550
551 // Duplicate field names must be aliased
552 if (fieldNames.value(fieldList[i]) > 1) {
553 QString relTableName = relation.tableName().section(QChar::fromLatin1('.'), -1, -1);
554 if (d->db.driver()->isIdentifierEscaped(relTableName, QSqlDriver::TableName))
555 relTableName = d->db.driver()->stripDelimiters(relTableName, QSqlDriver::TableName);
556 QString displayColumn = relation.displayColumn();
557 if (d->db.driver()->isIdentifierEscaped(displayColumn, QSqlDriver::FieldName))
558 displayColumn = d->db.driver()->stripDelimiters(displayColumn, QSqlDriver::FieldName);
559 QString alias = QString::fromLatin1("%1_%2_%3")
560 .arg(relTableName, displayColumn, QString::number(fieldNames.value(fieldList[i])));
561 alias.truncate(d->db.driver()->maximumIdentifierLength(QSqlDriver::FieldName));
562 alias = d->db.driver()->escapeIdentifier(alias, QSqlDriver::FieldName);
563 displayTableField = SqlrTm::as(displayTableField, alias);
564 --fieldNames[fieldList[i]];
565 }
566
567 fList = SqlrTm::comma(fList, displayTableField);
568
569 // Join related table
570 const QString tblexpr = SqlrTm::concat(relation.tableName(), relTableAlias);
571 const QString relTableField = d->fullyQualifiedFieldName(relTableAlias, relation.indexColumn());
572 const QString cond = SqlrTm::eq(tableField, 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(from, tblexpr);
577 conditions = SqlrTm::et(conditions, cond);
578 } else {
579 from = SqlrTm::concat(from, SqlrTm::leftJoin(tblexpr));
580 from = SqlrTm::concat(from, SqlrTm::on(cond));
581 }
582 } else {
583 fList = SqlrTm::comma(fList, tableField);
584 }
585 }
586
587 if (fList.isEmpty())
588 return QString();
589
590 const QString stmt = SqlrTm::concat(SqlrTm::select(fList), from);
591 const QString where = SqlrTm::where(SqlrTm::et(SqlrTm::paren(conditions), SqlrTm::paren(filter())));
592 return SqlrTm::concat(SqlrTm::concat(stmt, where), orderByClause());
593}
594
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
626
631{
634 d->clearChanges();
635 d->relations.clear();
638}
639
640
657{
659 d->joinMode = joinMode;
660}
668
673{
675
676 // memorize the table before applying the relations
677 d->baseRec = d->db.record(table);
678
680}
681
685{
686 for (int i = 0; i < values.count(); ++i) {
687 if (relations.value(i).isValid()) {
689 bool gen = values.isGenerated(i);
690 values.replace(i, baseRec.field(i));
691 values.setValue(i, v);
692 values.setGenerated(i, gen);
693 }
694 }
695}
696
701{
703
704 QSqlRecord rec = values;
705 d->translateFieldNames(rec);
706
708}
709
714{
716
717 QSqlRecord rec = values;
718 d->translateFieldNames(rec);
719
721}
722
727{
728 Q_D(const QSqlRelationalTableModel);
729
730 const QSqlRelation rel = d->relations.value(d->sortColumn).rel;
731 if (!rel.isValid())
733
734 QString f = d->fullyQualifiedFieldName(SqlrTm::relTablePrefix(d->sortColumn), rel.displayColumn());
735 f = d->sortOrder == Qt::AscendingOrder ? SqlrTm::asc(f) : SqlrTm::desc(f);
736 return SqlrTm::orderBy(f);
737}
738
743{
745
746 if (parent.isValid() || column < 0 || column + count > d->rec.count())
747 return false;
748
749 for (int i = 0; i < count; ++i) {
750 d->baseRec.remove(column);
751 if (d->relations.size() > column)
752 d->relations.remove(column);
753 }
755}
756
758
759#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:951
qsizetype size() const noexcept
Definition qlist.h:397
T value(qsizetype i) const
Definition qlist.h:664
\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.
QString fieldName(int i) const
Returns the name of the field at position index.
int indexOf(const QString &name) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
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:5871
void truncate(qsizetype pos)
Truncates the string at the given position index.
Definition qstring.cpp:6319
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:8084
QString & append(QChar c)
Definition qstring.cpp:3252
\inmodule QtCore
Definition qvariant.h:65
T value() const &
Definition qvariant.h:516
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)
QHash< QString, QVariant > dictionary