1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtSql module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qsqltablemodel.h"
41
42#include "qsqldriver.h"
43#include "qsqlerror.h"
44#include "qsqlfield.h"
45#include "qsqlindex.h"
46#include "qsqlquery.h"
47#include "qsqlrecord.h"
48#include "qsqlresult.h"
49
50#include "qsqltablemodel_p.h"
51
52#include <qdebug.h>
53
54QT_BEGIN_NAMESPACE
55
56typedef QSqlTableModelSql Sql;
57
58QSqlTableModelPrivate::~QSqlTableModelPrivate()
59{
60
61}
62
63/*! \internal
64 Populates our record with values.
65*/
66QSqlRecord QSqlTableModelPrivate::record(const QVector<QVariant> &values) const
67{
68 QSqlRecord r = rec;
69 for (int i = 0; i < r.count() && i < values.count(); ++i)
70 r.setValue(i, val: values.at(i));
71 return r;
72}
73
74int QSqlTableModelPrivate::nameToIndex(const QString &name) const
75{
76 return rec.indexOf(name: strippedFieldName(name));
77}
78
79QString QSqlTableModelPrivate::strippedFieldName(const QString &name) const
80{
81 QString fieldname = name;
82 if (db.driver()->isIdentifierEscaped(identifier: fieldname, type: QSqlDriver::FieldName))
83 fieldname = db.driver()->stripDelimiters(identifier: fieldname, type: QSqlDriver::FieldName);
84 return fieldname;
85}
86
87int QSqlTableModelPrivate::insertCount(int maxRow) const
88{
89 int cnt = 0;
90 CacheMap::ConstIterator i = cache.constBegin();
91 const CacheMap::ConstIterator e = cache.constEnd();
92 for ( ; i != e && (maxRow < 0 || i.key() <= maxRow); ++i)
93 if (i.value().insert())
94 ++cnt;
95
96 return cnt;
97}
98
99void QSqlTableModelPrivate::initRecordAndPrimaryIndex()
100{
101 rec = db.record(tablename: tableName);
102 primaryIndex = db.primaryIndex(tablename: tableName);
103 initColOffsets(size: rec.count());
104}
105
106void QSqlTableModelPrivate::clear()
107{
108 sortColumn = -1;
109 sortOrder = Qt::AscendingOrder;
110 tableName.clear();
111 editQuery.clear();
112 cache.clear();
113 primaryIndex.clear();
114 rec.clear();
115 filter.clear();
116}
117
118void QSqlTableModelPrivate::clearCache()
119{
120 cache.clear();
121}
122
123void QSqlTableModelPrivate::revertCachedRow(int row)
124{
125 Q_Q(QSqlTableModel);
126 ModifiedRow r = cache.value(akey: row);
127
128 switch (r.op()) {
129 case QSqlTableModelPrivate::None:
130 Q_ASSERT_X(false, "QSqlTableModelPrivate::revertCachedRow()", "Invalid entry in cache map");
131 return;
132 case QSqlTableModelPrivate::Update:
133 case QSqlTableModelPrivate::Delete:
134 if (!r.submitted()) {
135 cache[row].revert();
136 emit q->dataChanged(topLeft: q->createIndex(arow: row, acolumn: 0),
137 bottomRight: q->createIndex(arow: row, acolumn: q->columnCount() - 1));
138 }
139 break;
140 case QSqlTableModelPrivate::Insert: {
141 QMap<int, QSqlTableModelPrivate::ModifiedRow>::Iterator it = cache.find(akey: row);
142 if (it == cache.end())
143 return;
144 q->beginRemoveRows(parent: QModelIndex(), first: row, last: row);
145 it = cache.erase(it);
146 while (it != cache.end()) {
147 int oldKey = it.key();
148 const QSqlTableModelPrivate::ModifiedRow oldValue = it.value();
149 cache.erase(it);
150 it = cache.insert(akey: oldKey - 1, avalue: oldValue);
151 ++it;
152 }
153 q->endRemoveRows();
154 break; }
155 }
156}
157
158bool QSqlTableModelPrivate::exec(const QString &stmt, bool prepStatement,
159 const QSqlRecord &rec, const QSqlRecord &whereValues)
160{
161 if (stmt.isEmpty())
162 return false;
163
164 // lazy initialization of editQuery
165 if (editQuery.driver() != db.driver())
166 editQuery = QSqlQuery(db);
167
168 // workaround for In-Process databases - remove all read locks
169 // from the table to make sure the editQuery succeeds
170 if (db.driver()->hasFeature(f: QSqlDriver::SimpleLocking))
171 const_cast<QSqlResult *>(query.result())->detachFromResultSet();
172
173 if (prepStatement) {
174 if (editQuery.lastQuery() != stmt) {
175 if (!editQuery.prepare(query: stmt)) {
176 error = editQuery.lastError();
177 return false;
178 }
179 }
180 int i;
181 for (i = 0; i < rec.count(); ++i)
182 if (rec.isGenerated(i))
183 editQuery.addBindValue(val: rec.value(i));
184 for (i = 0; i < whereValues.count(); ++i)
185 if (whereValues.isGenerated(i) && !whereValues.isNull(i))
186 editQuery.addBindValue(val: whereValues.value(i));
187
188 if (!editQuery.exec()) {
189 error = editQuery.lastError();
190 return false;
191 }
192 } else {
193 if (!editQuery.exec(query: stmt)) {
194 error = editQuery.lastError();
195 return false;
196 }
197 }
198 return true;
199}
200
201/*!
202 \class QSqlTableModel
203 \brief The QSqlTableModel class provides an editable data model
204 for a single database table.
205
206 \ingroup database
207 \inmodule QtSql
208
209 QSqlTableModel is a high-level interface for reading and writing
210 database records from a single table. It is built on top of the
211 lower-level QSqlQuery and can be used to provide data to view
212 classes such as QTableView. For example:
213
214 \snippet sqldatabase/sqldatabase_snippet.cpp 24
215
216 We set the SQL table's name and the edit strategy, then we set up
217 the labels displayed in the view header. The edit strategy
218 dictates when the changes done by the user in the view are
219 actually applied to the database. The possible values are \l
220 OnFieldChange, \l OnRowChange, and \l OnManualSubmit.
221
222 QSqlTableModel can also be used to access a database
223 programmatically, without binding it to a view:
224
225 \snippet sqldatabase/sqldatabase.cpp 25
226
227 The code snippet above extracts the \c salary field from record 4 in
228 the result set of the query \c{SELECT * from employee}.
229
230 It is possible to set filters using setFilter(), or modify the
231 sort order using setSort(). At the end, you must call select() to
232 populate the model with data.
233
234 The \l{tablemodel} example illustrates how to use
235 QSqlTableModel as the data source for a QTableView.
236
237 QSqlTableModel provides no direct support for foreign keys. Use
238 the QSqlRelationalTableModel and QSqlRelationalDelegate if you
239 want to resolve foreign keys.
240
241 \sa QSqlRelationalTableModel, QSqlQuery, {Model/View Programming},
242 {Table Model Example}, {Cached Table Example}
243*/
244
245/*!
246 \fn QSqlTableModel::beforeDelete(int row)
247
248 This signal is emitted by deleteRowFromTable() before the \a row
249 is deleted from the currently active database table.
250*/
251
252/*!
253 \fn void QSqlTableModel::primeInsert(int row, QSqlRecord &record)
254
255 This signal is emitted by insertRows(), when an insertion is
256 initiated in the given \a row of the currently active database
257 table. The \a record parameter can be written to (since it is a
258 reference), for example to populate some fields with default
259 values and set the generated flags of the fields. Do not try to
260 edit the record via other means such as setData() or setRecord()
261 while handling this signal.
262*/
263
264/*!
265 \fn QSqlTableModel::beforeInsert(QSqlRecord &record)
266
267 This signal is emitted by insertRowIntoTable() before a new row is
268 inserted into the currently active database table. The values that
269 are about to be inserted are stored in \a record and can be
270 modified before they will be inserted.
271*/
272
273/*!
274 \fn QSqlTableModel::beforeUpdate(int row, QSqlRecord &record)
275
276 This signal is emitted by updateRowInTable() before the \a row is
277 updated in the currently active database table with the values
278 from \a record.
279
280 Note that only values that are marked as generated will be updated.
281 The generated flag can be set with \l QSqlRecord::setGenerated()
282 and checked with \l QSqlRecord::isGenerated().
283
284 \sa QSqlRecord::isGenerated()
285*/
286
287/*!
288 Creates an empty QSqlTableModel and sets the parent to \a parent
289 and the database connection to \a db. If \a db is not valid, the
290 default database connection will be used.
291
292 The default edit strategy is \l OnRowChange.
293*/
294QSqlTableModel::QSqlTableModel(QObject *parent, QSqlDatabase db)
295 : QSqlQueryModel(*new QSqlTableModelPrivate, parent)
296{
297 Q_D(QSqlTableModel);
298 d->db = db.isValid() ? db : QSqlDatabase::database();
299}
300
301/*! \internal
302*/
303QSqlTableModel::QSqlTableModel(QSqlTableModelPrivate &dd, QObject *parent, QSqlDatabase db)
304 : QSqlQueryModel(dd, parent)
305{
306 Q_D(QSqlTableModel);
307 d->db = db.isValid() ? db : QSqlDatabase::database();
308}
309
310/*!
311 Destroys the object and frees any allocated resources.
312*/
313QSqlTableModel::~QSqlTableModel()
314{
315}
316
317/*!
318 Sets the database table on which the model operates to \a
319 tableName. Does not select data from the table, but fetches its
320 field information.
321
322 To populate the model with the table's data, call select().
323
324 Error information can be retrieved with \l lastError().
325
326 \sa select(), setFilter(), lastError()
327*/
328void QSqlTableModel::setTable(const QString &tableName)
329{
330 Q_D(QSqlTableModel);
331 clear();
332 d->tableName = tableName;
333 d->initRecordAndPrimaryIndex();
334
335 if (d->rec.count() == 0)
336 d->error = QSqlError(QLatin1String("Unable to find table ") + d->tableName, QString(),
337 QSqlError::StatementError);
338
339 // Remember the auto index column if there is one now.
340 // The record that will be obtained from the query after select lacks this feature.
341 d->autoColumn.clear();
342 for (int c = 0; c < d->rec.count(); ++c) {
343 if (d->rec.field(i: c).isAutoValue()) {
344 d->autoColumn = d->rec.fieldName(i: c);
345 break;
346 }
347 }
348}
349
350/*!
351 Returns the name of the currently selected table.
352*/
353QString QSqlTableModel::tableName() const
354{
355 Q_D(const QSqlTableModel);
356 return d->tableName;
357}
358
359/*!
360 Populates the model with data from the table that was set via setTable(), using the
361 specified filter and sort condition, and returns \c true if successful; otherwise
362 returns \c false.
363
364 \note Calling select() will revert any unsubmitted changes and remove any inserted columns.
365
366 \sa setTable(), setFilter(), selectStatement()
367*/
368bool QSqlTableModel::select()
369{
370 Q_D(QSqlTableModel);
371 const QString query = selectStatement();
372 if (query.isEmpty())
373 return false;
374
375 beginResetModel();
376
377 d->clearCache();
378
379 QSqlQuery qu(query, d->db);
380 setQuery(qu);
381
382 if (!qu.isActive() || lastError().isValid()) {
383 // something went wrong - revert to non-select state
384 d->initRecordAndPrimaryIndex();
385 endResetModel();
386 return false;
387 }
388 endResetModel();
389 return true;
390}
391
392/*!
393 \since 5.0
394
395 Refreshes \a row in the model with values from the database table row matching
396 on primary key values. Without a primary key, all column values must match. If
397 no matching row is found, the model will show an empty row.
398
399 Returns \c true if successful; otherwise returns \c false.
400
401 \sa select()
402*/
403bool QSqlTableModel::selectRow(int row)
404{
405 Q_D(QSqlTableModel);
406
407 if (row < 0 || row >= rowCount())
408 return false;
409
410 const int table_sort_col = d->sortColumn;
411 d->sortColumn = -1;
412 const QString table_filter = d->filter;
413 d->filter = d->db.driver()->sqlStatement(type: QSqlDriver::WhereStatement,
414 tableName: d->tableName,
415 rec: primaryValues(row),
416 preparedStatement: false);
417 static const QString wh = Sql::where() + Sql::sp();
418 if (d->filter.startsWith(s: wh, cs: Qt::CaseInsensitive))
419 d->filter.remove(i: 0, len: wh.length());
420
421 QString stmt;
422
423 if (!d->filter.isEmpty())
424 stmt = selectStatement();
425
426 d->sortColumn = table_sort_col;
427 d->filter = table_filter;
428
429 if (stmt.isEmpty())
430 return false;
431
432 bool exists;
433 QSqlRecord newValues;
434
435 {
436 QSqlQuery q(d->db);
437 q.setForwardOnly(true);
438 if (!q.exec(query: stmt))
439 return false;
440
441 exists = q.next();
442 newValues = q.record();
443 }
444
445 bool needsAddingToCache = !exists || d->cache.contains(akey: row);
446
447 if (!needsAddingToCache) {
448 const QSqlRecord curValues = record(row);
449 needsAddingToCache = curValues.count() != newValues.count();
450 if (!needsAddingToCache) {
451 // Look for changed values. Primary key fields are customarily first
452 // and probably change less often than other fields, so start at the end.
453 for (int f = curValues.count() - 1; f >= 0; --f) {
454 if (curValues.value(i: f) != newValues.value(i: f)) {
455 needsAddingToCache = true;
456 break;
457 }
458 }
459 }
460 }
461
462 if (needsAddingToCache) {
463 d->cache[row].refresh(exists, newvals: newValues);
464 emit headerDataChanged(orientation: Qt::Vertical, first: row, last: row);
465 emit dataChanged(topLeft: createIndex(arow: row, acolumn: 0), bottomRight: createIndex(arow: row, acolumn: columnCount() - 1));
466 }
467
468 return true;
469}
470
471/*!
472 \reimp
473*/
474QVariant QSqlTableModel::data(const QModelIndex &index, int role) const
475{
476 Q_D(const QSqlTableModel);
477 if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::EditRole))
478 return QVariant();
479
480 const auto it = d->cache.constFind(akey: index.row());
481 if (it != d->cache.constEnd() && it->op() != QSqlTableModelPrivate::None)
482 return it->rec().value(i: index.column());
483
484 return QSqlQueryModel::data(item: index, role);
485}
486
487/*!
488 \reimp
489*/
490QVariant QSqlTableModel::headerData(int section, Qt::Orientation orientation, int role) const
491{
492 Q_D(const QSqlTableModel);
493 if (orientation == Qt::Vertical && role == Qt::DisplayRole) {
494 const QSqlTableModelPrivate::Op op = d->cache.value(akey: section).op();
495 if (op == QSqlTableModelPrivate::Insert)
496 return QLatin1String("*");
497 else if (op == QSqlTableModelPrivate::Delete)
498 return QLatin1String("!");
499 }
500 return QSqlQueryModel::headerData(section, orientation, role);
501}
502
503/*!
504 \overload
505 \since 5.0
506
507 Returns \c true if the model contains modified values that have not been
508 committed to the database, otherwise false.
509*/
510bool QSqlTableModel::isDirty() const
511{
512 Q_D(const QSqlTableModel);
513 QSqlTableModelPrivate::CacheMap::ConstIterator i = d->cache.constBegin();
514 const QSqlTableModelPrivate::CacheMap::ConstIterator e = d->cache.constEnd();
515 for (; i != e; ++i) {
516 if (!i.value().submitted())
517 return true;
518 }
519 return false;
520}
521
522/*!
523 Returns \c true if the value at the index \a index is dirty, otherwise false.
524 Dirty values are values that were modified in the model
525 but not yet written into the database.
526
527 If \a index is invalid or points to a non-existing row, false is returned.
528*/
529bool QSqlTableModel::isDirty(const QModelIndex &index) const
530{
531 Q_D(const QSqlTableModel);
532 if (!index.isValid())
533 return false;
534
535 const auto it = d->cache.constFind(akey: index.row());
536 if (it == d->cache.constEnd())
537 return false;
538 const QSqlTableModelPrivate::ModifiedRow &row = *it;
539 if (row.submitted())
540 return false;
541
542 return row.op() == QSqlTableModelPrivate::Insert
543 || row.op() == QSqlTableModelPrivate::Delete
544 || (row.op() == QSqlTableModelPrivate::Update
545 && row.rec().isGenerated(i: index.column()));
546}
547
548/*!
549 Sets the data for the item \a index for the role \a role to \a
550 value.
551
552 For edit strategy OnFieldChange, an index may receive a change
553 only if no other index has a cached change. Changes are
554 submitted immediately. However, rows that have not yet been
555 inserted in the database may be freely changed and are not
556 submitted automatically. Submitted changes are not reverted upon
557 failure.
558
559 For OnRowChange, an index may receive a change only if no other
560 row has a cached change. Changes are not submitted automatically.
561
562 Returns \c true if \a value is equal to the current value. However,
563 the value will not be submitted to the database.
564
565 Returns \c true if the value could be set or false on error, for
566 example if \a index is out of bounds.
567
568 Returns \c false if the role is not Qt::EditRole. To set data
569 for roles other than EditRole, either use a custom proxy model
570 or subclass QSqlTableModel.
571
572 \sa editStrategy(), data(), submit(), submitAll(), revertRow()
573*/
574bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
575{
576 Q_D(QSqlTableModel);
577 if (d->busyInsertingRows)
578 return false;
579
580 if (role != Qt::EditRole)
581 return QSqlQueryModel::setData(index, value, role);
582
583 if (!index.isValid() || index.column() >= d->rec.count() || index.row() >= rowCount())
584 return false;
585
586 if (!(flags(index) & Qt::ItemIsEditable))
587 return false;
588
589 const QVariant oldValue = QSqlTableModel::data(index, role);
590 if (value == oldValue
591 && value.isNull() == oldValue.isNull()
592 && d->cache.value(akey: index.row()).op() != QSqlTableModelPrivate::Insert)
593 return true;
594
595 QSqlTableModelPrivate::ModifiedRow &row = d->cache[index.row()];
596
597 if (row.op() == QSqlTableModelPrivate::None)
598 row = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Update,
599 QSqlQueryModel::record(row: index.row()));
600
601 row.setValue(c: index.column(), v: value);
602 emit dataChanged(topLeft: index, bottomRight: index);
603
604 if (d->strategy == OnFieldChange && row.op() != QSqlTableModelPrivate::Insert)
605 return submit();
606
607 return true;
608}
609
610#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
611/*!
612 \reimp
613 */
614bool QSqlTableModel::clearItemData(const QModelIndex &index)
615{
616 return setData(index, QVariant(), Qt::EditRole);
617}
618#endif
619
620/*!
621 This function simply calls QSqlQueryModel::setQuery(\a query).
622 You should normally not call it on a QSqlTableModel. Instead, use
623 setTable(), setSort(), setFilter(), etc., to set up the query.
624
625 \sa selectStatement()
626*/
627void QSqlTableModel::setQuery(const QSqlQuery &query)
628{
629 QSqlQueryModel::setQuery(query);
630}
631
632/*!
633 Updates the given \a row in the currently active database table
634 with the specified \a values. Returns \c true if successful; otherwise
635 returns \c false.
636
637 This is a low-level method that operates directly on the database
638 and should not be called directly. Use setData() to update values.
639 The model will decide depending on its edit strategy when to modify
640 the database.
641
642 Note that only values that have the generated-flag set are updated.
643 The generated-flag can be set with QSqlRecord::setGenerated() and
644 tested with QSqlRecord::isGenerated().
645
646 \sa QSqlRecord::isGenerated(), setData()
647*/
648bool QSqlTableModel::updateRowInTable(int row, const QSqlRecord &values)
649{
650 Q_D(QSqlTableModel);
651 QSqlRecord rec(values);
652 emit beforeUpdate(row, record&: rec);
653
654 const QSqlRecord whereValues = primaryValues(row);
655 const bool prepStatement = d->db.driver()->hasFeature(f: QSqlDriver::PreparedQueries);
656 const QString stmt = d->db.driver()->sqlStatement(type: QSqlDriver::UpdateStatement, tableName: d->tableName,
657 rec, preparedStatement: prepStatement);
658 const QString where = d->db.driver()->sqlStatement(type: QSqlDriver::WhereStatement, tableName: d->tableName,
659 rec: whereValues, preparedStatement: prepStatement);
660
661 if (stmt.isEmpty() || where.isEmpty() || row < 0 || row >= rowCount()) {
662 d->error = QSqlError(QLatin1String("No Fields to update"), QString(),
663 QSqlError::StatementError);
664 return false;
665 }
666
667 return d->exec(stmt: Sql::concat(a: stmt, b: where), prepStatement, rec, whereValues);
668}
669
670
671/*!
672 Inserts the values \a values into the currently active database table.
673
674 This is a low-level method that operates directly on the database
675 and should not be called directly. Use insertRow() and setData()
676 to insert values. The model will decide depending on its edit strategy
677 when to modify the database.
678
679 Returns \c true if the values could be inserted, otherwise false.
680 Error information can be retrieved with \l lastError().
681
682 \sa lastError(), insertRow(), insertRows()
683*/
684bool QSqlTableModel::insertRowIntoTable(const QSqlRecord &values)
685{
686 Q_D(QSqlTableModel);
687 QSqlRecord rec = values;
688 emit beforeInsert(record&: rec);
689
690 const bool prepStatement = d->db.driver()->hasFeature(f: QSqlDriver::PreparedQueries);
691 const QString stmt = d->db.driver()->sqlStatement(type: QSqlDriver::InsertStatement, tableName: d->tableName,
692 rec, preparedStatement: prepStatement);
693
694 if (stmt.isEmpty()) {
695 d->error = QSqlError(QLatin1String("No Fields to update"), QString(),
696 QSqlError::StatementError);
697 return false;
698 }
699
700 return d->exec(stmt, prepStatement, rec, whereValues: QSqlRecord() /* no where values */);
701}
702
703/*!
704 Deletes the given \a row from the currently active database table.
705
706 This is a low-level method that operates directly on the database
707 and should not be called directly. Use removeRow() or removeRows()
708 to delete values. The model will decide depending on its edit strategy
709 when to modify the database.
710
711 Returns \c true if the row was deleted; otherwise returns \c false.
712
713 \sa removeRow(), removeRows()
714*/
715bool QSqlTableModel::deleteRowFromTable(int row)
716{
717 Q_D(QSqlTableModel);
718 emit beforeDelete(row);
719
720 const QSqlRecord whereValues = primaryValues(row);
721 const bool prepStatement = d->db.driver()->hasFeature(f: QSqlDriver::PreparedQueries);
722 const QString stmt = d->db.driver()->sqlStatement(type: QSqlDriver::DeleteStatement,
723 tableName: d->tableName,
724 rec: QSqlRecord(),
725 preparedStatement: prepStatement);
726 const QString where = d->db.driver()->sqlStatement(type: QSqlDriver::WhereStatement,
727 tableName: d->tableName,
728 rec: whereValues,
729 preparedStatement: prepStatement);
730
731 if (stmt.isEmpty() || where.isEmpty()) {
732 d->error = QSqlError(QLatin1String("Unable to delete row"), QString(),
733 QSqlError::StatementError);
734 return false;
735 }
736
737 return d->exec(stmt: Sql::concat(a: stmt, b: where), prepStatement, rec: QSqlRecord() /* no new values */, whereValues);
738}
739
740/*!
741 Submits all pending changes and returns \c true on success.
742 Returns \c false on error, detailed error information can be
743 obtained with lastError().
744
745 In OnManualSubmit, on success the model will be repopulated.
746 Any views presenting it will lose their selections.
747
748 Note: In OnManualSubmit mode, already submitted changes won't
749 be cleared from the cache when submitAll() fails. This allows
750 transactions to be rolled back and resubmitted without
751 losing data.
752
753 \sa revertAll(), lastError()
754*/
755bool QSqlTableModel::submitAll()
756{
757 Q_D(QSqlTableModel);
758
759 bool success = true;
760
761 const auto cachedKeys = d->cache.keys();
762 for (int row : cachedKeys) {
763 // be sure cache *still* contains the row since overridden selectRow() could have called select()
764 QSqlTableModelPrivate::CacheMap::iterator it = d->cache.find(akey: row);
765 if (it == d->cache.end())
766 continue;
767
768 QSqlTableModelPrivate::ModifiedRow &mrow = it.value();
769 if (mrow.submitted())
770 continue;
771
772 switch (mrow.op()) {
773 case QSqlTableModelPrivate::Insert:
774 success = insertRowIntoTable(values: mrow.rec());
775 break;
776 case QSqlTableModelPrivate::Update:
777 success = updateRowInTable(row, values: mrow.rec());
778 break;
779 case QSqlTableModelPrivate::Delete:
780 success = deleteRowFromTable(row);
781 break;
782 case QSqlTableModelPrivate::None:
783 Q_ASSERT_X(false, "QSqlTableModel::submitAll()", "Invalid cache operation");
784 break;
785 }
786
787 if (success) {
788 if (d->strategy != OnManualSubmit && mrow.op() == QSqlTableModelPrivate::Insert) {
789 int c = mrow.rec().indexOf(name: d->autoColumn);
790 if (c != -1 && !mrow.rec().isGenerated(i: c))
791 mrow.setValue(c, v: d->editQuery.lastInsertId());
792 }
793 mrow.setSubmitted();
794 if (d->strategy != OnManualSubmit)
795 success = selectRow(row);
796 }
797
798 if (!success)
799 break;
800 }
801
802 if (success) {
803 if (d->strategy == OnManualSubmit)
804 success = select();
805 }
806
807 return success;
808}
809
810/*!
811 This reimplemented slot is called by the item delegates when the
812 user stopped editing the current row.
813
814 Submits the currently edited row if the model's strategy is set
815 to OnRowChange or OnFieldChange. Does nothing for the OnManualSubmit
816 strategy.
817
818 Use submitAll() to submit all pending changes for the
819 OnManualSubmit strategy.
820
821 Returns \c true on success; otherwise returns \c false. Use lastError()
822 to query detailed error information.
823
824 Does not automatically repopulate the model. Submitted rows are
825 refreshed from the database on success.
826
827 \sa revert(), revertRow(), submitAll(), revertAll(), lastError()
828*/
829bool QSqlTableModel::submit()
830{
831 Q_D(QSqlTableModel);
832 if (d->strategy == OnRowChange || d->strategy == OnFieldChange)
833 return submitAll();
834 return true;
835}
836
837/*!
838 This reimplemented slot is called by the item delegates when the
839 user canceled editing the current row.
840
841 Reverts the changes if the model's strategy is set to
842 OnRowChange or OnFieldChange. Does nothing for the OnManualSubmit
843 strategy.
844
845 Use revertAll() to revert all pending changes for the
846 OnManualSubmit strategy or revertRow() to revert a specific row.
847
848 \sa submit(), submitAll(), revertRow(), revertAll()
849*/
850void QSqlTableModel::revert()
851{
852 Q_D(QSqlTableModel);
853 if (d->strategy == OnRowChange || d->strategy == OnFieldChange)
854 revertAll();
855}
856
857/*!
858 \enum QSqlTableModel::EditStrategy
859
860 This enum type describes which strategy to choose when editing values in the database.
861
862 \value OnFieldChange All changes to the model will be applied immediately to the database.
863 \value OnRowChange Changes to a row will be applied when the user selects a different row.
864 \value OnManualSubmit All changes will be cached in the model until either submitAll()
865 or revertAll() is called.
866
867 Note: To prevent inserting only partly initialized rows into the database,
868 \c OnFieldChange will behave like \c OnRowChange for newly inserted rows.
869
870 \sa setEditStrategy()
871*/
872
873
874/*!
875 Sets the strategy for editing values in the database to \a
876 strategy.
877
878 This will revert any pending changes.
879
880 \sa editStrategy(), revertAll()
881*/
882void QSqlTableModel::setEditStrategy(EditStrategy strategy)
883{
884 Q_D(QSqlTableModel);
885 revertAll();
886 d->strategy = strategy;
887}
888
889/*!
890 Returns the current edit strategy.
891
892 \sa setEditStrategy()
893*/
894QSqlTableModel::EditStrategy QSqlTableModel::editStrategy() const
895{
896 Q_D(const QSqlTableModel);
897 return d->strategy;
898}
899
900/*!
901 Reverts all pending changes.
902
903 \sa revert(), revertRow(), submitAll()
904*/
905void QSqlTableModel::revertAll()
906{
907 Q_D(QSqlTableModel);
908
909 const QList<int> rows(d->cache.keys());
910 for (int i = rows.size() - 1; i >= 0; --i)
911 revertRow(row: rows.value(i));
912}
913
914/*!
915 Reverts all changes for the specified \a row.
916
917 \sa revert(), revertAll(), submit(), submitAll()
918*/
919void QSqlTableModel::revertRow(int row)
920{
921 if (row < 0)
922 return;
923
924 Q_D(QSqlTableModel);
925 d->revertCachedRow(row);
926}
927
928/*!
929 Returns the primary key for the current table, or an empty
930 QSqlIndex if the table is not set or has no primary key.
931
932 \sa setTable(), setPrimaryKey(), QSqlDatabase::primaryIndex()
933*/
934QSqlIndex QSqlTableModel::primaryKey() const
935{
936 Q_D(const QSqlTableModel);
937 return d->primaryIndex;
938}
939
940/*!
941 Protected method that allows subclasses to set the primary key to
942 \a key.
943
944 Normally, the primary index is set automatically whenever you
945 call setTable().
946
947 \sa primaryKey(), QSqlDatabase::primaryIndex()
948*/
949void QSqlTableModel::setPrimaryKey(const QSqlIndex &key)
950{
951 Q_D(QSqlTableModel);
952 d->primaryIndex = key;
953}
954
955/*!
956 Returns the model's database connection.
957*/
958QSqlDatabase QSqlTableModel::database() const
959{
960 Q_D(const QSqlTableModel);
961 return d->db;
962}
963
964/*!
965 Sorts the data by \a column with the sort order \a order.
966 This will immediately select data, use setSort()
967 to set a sort order without populating the model with data.
968
969 \sa setSort(), select(), orderByClause()
970*/
971void QSqlTableModel::sort(int column, Qt::SortOrder order)
972{
973 setSort(column, order);
974 select();
975}
976
977/*!
978 Sets the sort order for \a column to \a order. This does not
979 affect the current data, to refresh the data using the new
980 sort order, call select().
981
982 \sa select(), orderByClause()
983*/
984void QSqlTableModel::setSort(int column, Qt::SortOrder order)
985{
986 Q_D(QSqlTableModel);
987 d->sortColumn = column;
988 d->sortOrder = order;
989}
990
991/*!
992 Returns an SQL \c{ORDER BY} clause based on the currently set
993 sort order.
994
995 \sa setSort(), selectStatement()
996*/
997QString QSqlTableModel::orderByClause() const
998{
999 Q_D(const QSqlTableModel);
1000 QSqlField f = d->rec.field(i: d->sortColumn);
1001 if (!f.isValid())
1002 return QString();
1003
1004 //we can safely escape the field because it would have been obtained from the database
1005 //and have the correct case
1006 QString field = d->db.driver()->escapeIdentifier(identifier: d->tableName, type: QSqlDriver::TableName)
1007 + QLatin1Char('.')
1008 + d->db.driver()->escapeIdentifier(identifier: f.name(), type: QSqlDriver::FieldName);
1009 field = d->sortOrder == Qt::AscendingOrder ? Sql::asc(s: field) : Sql::desc(s: field);
1010 return Sql::orderBy(s: field);
1011}
1012
1013/*!
1014 Returns the index of the field \a fieldName, or -1 if no corresponding field
1015 exists in the model.
1016*/
1017int QSqlTableModel::fieldIndex(const QString &fieldName) const
1018{
1019 Q_D(const QSqlTableModel);
1020 return d->rec.indexOf(name: fieldName);
1021}
1022
1023/*!
1024 Returns the SQL \c SELECT statement used internally to populate
1025 the model. The statement includes the filter and the \c{ORDER BY}
1026 clause.
1027
1028 \sa filter(), orderByClause()
1029*/
1030QString QSqlTableModel::selectStatement() const
1031{
1032 Q_D(const QSqlTableModel);
1033 if (d->tableName.isEmpty()) {
1034 d->error = QSqlError(QLatin1String("No table name given"), QString(),
1035 QSqlError::StatementError);
1036 return QString();
1037 }
1038 if (d->rec.isEmpty()) {
1039 d->error = QSqlError(QLatin1String("Unable to find table ") + d->tableName, QString(),
1040 QSqlError::StatementError);
1041 return QString();
1042 }
1043
1044 const QString stmt = d->db.driver()->sqlStatement(type: QSqlDriver::SelectStatement,
1045 tableName: d->tableName,
1046 rec: d->rec,
1047 preparedStatement: false);
1048 if (stmt.isEmpty()) {
1049 d->error = QSqlError(QLatin1String("Unable to select fields from table ") + d->tableName,
1050 QString(), QSqlError::StatementError);
1051 return stmt;
1052 }
1053 return Sql::concat(a: Sql::concat(a: stmt, b: Sql::where(s: d->filter)), b: orderByClause());
1054}
1055
1056/*!
1057 Removes \a count columns from the \a parent model, starting at
1058 index \a column.
1059
1060 Returns if the columns were successfully removed; otherwise
1061 returns \c false.
1062
1063 \sa removeRows()
1064*/
1065bool QSqlTableModel::removeColumns(int column, int count, const QModelIndex &parent)
1066{
1067 Q_D(QSqlTableModel);
1068 if (parent.isValid() || column < 0 || column + count > d->rec.count())
1069 return false;
1070 for (int i = 0; i < count; ++i)
1071 d->rec.remove(pos: column);
1072 if (d->query.isActive())
1073 return select();
1074 return true;
1075}
1076
1077/*!
1078 Removes \a count rows starting at \a row. Since this model
1079 does not support hierarchical structures, \a parent must be
1080 an invalid model index.
1081
1082 When the edit strategy is OnManualSubmit, deletion of rows from
1083 the database is delayed until submitAll() is called.
1084
1085 For OnFieldChange and OnRowChange, only one row may be deleted
1086 at a time and only if no other row has a cached change. Deletions
1087 are submitted immediately to the database. The model retains a
1088 blank row for successfully deleted row until refreshed with select().
1089
1090 After failed deletion, the operation is not reverted in the model.
1091 The application may resubmit or revert.
1092
1093 Inserted but not yet successfully submitted rows in the range to be
1094 removed are immediately removed from the model.
1095
1096 Before a row is deleted from the database, the beforeDelete()
1097 signal is emitted.
1098
1099 If row < 0 or row + count > rowCount(), no action is taken and
1100 false is returned. Returns \c true if all rows could be removed;
1101 otherwise returns \c false. Detailed database error information
1102 can be retrieved using lastError().
1103
1104 \sa removeColumns(), insertRows()
1105*/
1106bool QSqlTableModel::removeRows(int row, int count, const QModelIndex &parent)
1107{
1108 Q_D(QSqlTableModel);
1109 if (parent.isValid() || row < 0 || count <= 0)
1110 return false;
1111 else if (row + count > rowCount())
1112 return false;
1113 else if (!count)
1114 return true;
1115
1116 if (d->strategy != OnManualSubmit)
1117 if (count > 1 || (d->cache.value(akey: row).submitted() && isDirty()))
1118 return false;
1119
1120 // Iterate backwards so we don't have to worry about removed rows causing
1121 // higher cache entries to shift downwards.
1122 for (int idx = row + count - 1; idx >= row; --idx) {
1123 QSqlTableModelPrivate::ModifiedRow& mrow = d->cache[idx];
1124 if (mrow.op() == QSqlTableModelPrivate::Insert) {
1125 revertRow(row: idx);
1126 } else {
1127 if (mrow.op() == QSqlTableModelPrivate::None)
1128 mrow = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Delete,
1129 QSqlQueryModel::record(row: idx));
1130 else
1131 mrow.setOp(QSqlTableModelPrivate::Delete);
1132 if (d->strategy == OnManualSubmit)
1133 emit headerDataChanged(orientation: Qt::Vertical, first: idx, last: idx);
1134 }
1135 }
1136
1137 if (d->strategy != OnManualSubmit)
1138 return submit();
1139
1140 return true;
1141}
1142
1143/*!
1144 Inserts \a count empty rows at position \a row. Note that \a
1145 parent must be invalid, since this model does not support
1146 parent-child relations.
1147
1148 For edit strategies OnFieldChange and OnRowChange, only one row
1149 may be inserted at a time and the model may not contain other
1150 cached changes.
1151
1152 The primeInsert() signal will be emitted for each new row.
1153 Connect to it if you want to initialize the new row with default
1154 values.
1155
1156 Does not submit rows, regardless of edit strategy.
1157
1158 Returns \c false if the parameters are out of bounds or the row cannot be
1159 inserted; otherwise returns \c true.
1160
1161 \sa primeInsert(), insertRecord()
1162*/
1163bool QSqlTableModel::insertRows(int row, int count, const QModelIndex &parent)
1164{
1165 Q_D(QSqlTableModel);
1166 if (row < 0 || count <= 0 || row > rowCount() || parent.isValid())
1167 return false;
1168
1169 if (d->strategy != OnManualSubmit)
1170 if (count != 1 || isDirty())
1171 return false;
1172
1173 d->busyInsertingRows = true;
1174 beginInsertRows(parent, first: row, last: row + count - 1);
1175
1176 if (d->strategy != OnManualSubmit)
1177 d->cache.empty();
1178
1179 if (!d->cache.isEmpty()) {
1180 QMap<int, QSqlTableModelPrivate::ModifiedRow>::Iterator it = d->cache.end();
1181 while (it != d->cache.begin() && (--it).key() >= row) {
1182 int oldKey = it.key();
1183 const QSqlTableModelPrivate::ModifiedRow oldValue = it.value();
1184 d->cache.erase(it);
1185 it = d->cache.insert(akey: oldKey + count, avalue: oldValue);
1186 }
1187 }
1188
1189 for (int i = 0; i < count; ++i) {
1190 d->cache[row + i] = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Insert,
1191 d->rec);
1192 emit primeInsert(row: row + i, record&: d->cache[row + i].recRef());
1193 }
1194
1195 endInsertRows();
1196 d->busyInsertingRows = false;
1197 return true;
1198}
1199
1200/*!
1201 Inserts the \a record at position \a row. If \a row is negative,
1202 the record will be appended to the end. Calls insertRows() and
1203 setRecord() internally.
1204
1205 Returns \c true if the record could be inserted, otherwise false.
1206
1207 Changes are submitted immediately for OnFieldChange and
1208 OnRowChange. Failure does not leave a new row in the model.
1209
1210 \sa insertRows(), removeRows(), setRecord()
1211*/
1212bool QSqlTableModel::insertRecord(int row, const QSqlRecord &record)
1213{
1214 if (row < 0)
1215 row = rowCount();
1216 if (!insertRow(arow: row, aparent: QModelIndex()))
1217 return false;
1218 if (!setRecord(row, record)) {
1219 revertRow(row);
1220 return false;
1221 }
1222 return true;
1223}
1224
1225/*! \reimp
1226*/
1227int QSqlTableModel::rowCount(const QModelIndex &parent) const
1228{
1229 Q_D(const QSqlTableModel);
1230
1231 if (parent.isValid())
1232 return 0;
1233
1234 return QSqlQueryModel::rowCount() + d->insertCount();
1235}
1236
1237/*!
1238 Returns the index of the value in the database result set for the
1239 given \a item in the model.
1240
1241 The return value is identical to \a item if no columns or rows
1242 have been inserted, removed, or moved around.
1243
1244 Returns an invalid model index if \a item is out of bounds or if
1245 \a item does not point to a value in the result set.
1246
1247 \sa QSqlQueryModel::indexInQuery()
1248*/
1249QModelIndex QSqlTableModel::indexInQuery(const QModelIndex &item) const
1250{
1251 Q_D(const QSqlTableModel);
1252 const auto it = d->cache.constFind(akey: item.row());
1253 if (it != d->cache.constEnd() && it->insert())
1254 return QModelIndex();
1255
1256 const int rowOffset = d->insertCount(maxRow: item.row());
1257 return QSqlQueryModel::indexInQuery(item: createIndex(arow: item.row() - rowOffset, acolumn: item.column(), adata: item.internalPointer()));
1258}
1259
1260/*!
1261 Returns the currently set filter.
1262
1263 \sa setFilter(), select()
1264*/
1265QString QSqlTableModel::filter() const
1266{
1267 Q_D(const QSqlTableModel);
1268 return d->filter;
1269}
1270
1271/*!
1272 Sets the current filter to \a filter.
1273
1274 The filter is a SQL \c WHERE clause without the keyword \c WHERE
1275 (for example, \c{name='Josephine')}.
1276
1277 If the model is already populated with data from a database,
1278 the model re-selects it with the new filter. Otherwise, the filter
1279 will be applied the next time select() is called.
1280
1281 \sa filter(), select(), selectStatement(), orderByClause()
1282*/
1283void QSqlTableModel::setFilter(const QString &filter)
1284{
1285 Q_D(QSqlTableModel);
1286 d->filter = filter;
1287 if (d->query.isActive())
1288 select();
1289}
1290
1291/*! \reimp
1292*/
1293void QSqlTableModel::clear()
1294{
1295 Q_D(QSqlTableModel);
1296 beginResetModel();
1297 d->clear();
1298 QSqlQueryModel::clear();
1299 endResetModel();
1300}
1301
1302/*! \reimp
1303*/
1304Qt::ItemFlags QSqlTableModel::flags(const QModelIndex &index) const
1305{
1306 Q_D(const QSqlTableModel);
1307 if (index.internalPointer() || index.column() < 0 || index.column() >= d->rec.count()
1308 || index.row() < 0)
1309 return { };
1310
1311 bool editable = true;
1312
1313 if (d->rec.field(i: index.column()).isReadOnly()) {
1314 editable = false;
1315 }
1316 else {
1317 const QSqlTableModelPrivate::ModifiedRow mrow = d->cache.value(akey: index.row());
1318 if (mrow.op() == QSqlTableModelPrivate::Delete) {
1319 editable = false;
1320 }
1321 else if (d->strategy == OnFieldChange) {
1322 if (mrow.op() != QSqlTableModelPrivate::Insert)
1323 if (!isDirty(index) && isDirty())
1324 editable = false;
1325 }
1326 else if (d->strategy == OnRowChange) {
1327 if (mrow.submitted() && isDirty())
1328 editable = false;
1329 }
1330 }
1331
1332 if (!editable)
1333 return QSqlQueryModel::flags(index);
1334 else
1335 return QSqlQueryModel::flags(index) | Qt::ItemIsEditable;
1336}
1337
1338/*!
1339 This is an overloaded function.
1340
1341 It returns an empty record, having only the field names. This function can be used to
1342 retrieve the field names of a record.
1343
1344 \sa QSqlRecord::isEmpty()
1345*/
1346QSqlRecord QSqlTableModel::record() const
1347{
1348 return QSqlQueryModel::record();
1349}
1350
1351/*!
1352\since 5.0
1353 Returns the record at \a row in the model.
1354
1355 If \a row is the index of a valid row, the record
1356 will be populated with values from that row.
1357
1358 If the model is not initialized, an empty record will be
1359 returned.
1360
1361 \sa QSqlRecord::isEmpty()
1362*/
1363QSqlRecord QSqlTableModel::record(int row) const
1364{
1365 Q_D(const QSqlTableModel);
1366
1367 // the query gets the values from virtual data()
1368 QSqlRecord rec = QSqlQueryModel::record(row);
1369
1370 // get generated flags from the cache
1371 const QSqlTableModelPrivate::ModifiedRow mrow = d->cache.value(akey: row);
1372 if (mrow.op() != QSqlTableModelPrivate::None) {
1373 const QSqlRecord &crec = mrow.rec();
1374 for (int i = 0, cnt = rec.count(); i < cnt; ++i)
1375 rec.setGenerated(i, generated: crec.isGenerated(i));
1376 }
1377
1378 return rec;
1379}
1380
1381/*!
1382 Applies \a values to the \a row in the model. The source and
1383 target fields are mapped by field name, not by position in
1384 the record.
1385
1386 Note that the generated flags in \a values are preserved to
1387 determine whether the corresponding fields are used when changes
1388 are submitted to the database. By default, it is set to \c true
1389 for all fields in a QSqlRecord. You must set the flag to \c false
1390 using \l{QSqlRecord::}{setGenerated}(false) for any value in
1391 \a values, to save changes back to the database.
1392
1393 For edit strategies OnFieldChange and OnRowChange, a row may
1394 receive a change only if no other row has a cached change.
1395 Changes are submitted immediately. Submitted changes are not
1396 reverted upon failure.
1397
1398 Returns \c true if all the values could be set; otherwise returns
1399 false.
1400
1401 \sa record(), editStrategy()
1402*/
1403bool QSqlTableModel::setRecord(int row, const QSqlRecord &values)
1404{
1405 Q_D(QSqlTableModel);
1406 Q_ASSERT_X(row >= 0, "QSqlTableModel::setRecord()", "Cannot set a record to a row less than 0");
1407 if (d->busyInsertingRows)
1408 return false;
1409
1410 if (row >= rowCount())
1411 return false;
1412
1413 if (d->cache.value(akey: row).op() == QSqlTableModelPrivate::Delete)
1414 return false;
1415
1416 if (d->strategy != OnManualSubmit && d->cache.value(akey: row).submitted() && isDirty())
1417 return false;
1418
1419 // Check field names and remember mapping
1420 typedef QMap<int, int> Map;
1421 Map map;
1422 for (int i = 0; i < values.count(); ++i) {
1423 int idx = d->nameToIndex(name: values.fieldName(i));
1424 if (idx == -1)
1425 return false;
1426 map[i] = idx;
1427 }
1428
1429 QSqlTableModelPrivate::ModifiedRow &mrow = d->cache[row];
1430 if (mrow.op() == QSqlTableModelPrivate::None)
1431 mrow = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Update,
1432 QSqlQueryModel::record(row));
1433
1434 Map::const_iterator i = map.constBegin();
1435 const Map::const_iterator e = map.constEnd();
1436 for ( ; i != e; ++i) {
1437 // have to use virtual setData() here rather than mrow.setValue()
1438 EditStrategy strategy = d->strategy;
1439 d->strategy = OnManualSubmit;
1440 QModelIndex cIndex = createIndex(arow: row, acolumn: i.value());
1441 setData(index: cIndex, value: values.value(i: i.key()));
1442 d->strategy = strategy;
1443 // setData() sets generated to TRUE, but source record should prevail.
1444 if (!values.isGenerated(i: i.key()))
1445 mrow.recRef().setGenerated(i: i.value(), generated: false);
1446 }
1447
1448 if (d->strategy != OnManualSubmit)
1449 return submit();
1450
1451 return true;
1452}
1453
1454/*!
1455 \since 5.1
1456 Returns a record containing the fields represented in the primary key set to the values
1457 at \a row. If no primary key is defined, the returned record will contain all fields.
1458
1459 \sa primaryKey()
1460*/
1461QSqlRecord QSqlTableModel::primaryValues(int row) const
1462{
1463 Q_D(const QSqlTableModel);
1464
1465 const QSqlRecord &pIndex = d->primaryIndex.isEmpty() ? d->rec : d->primaryIndex;
1466
1467 QSqlTableModelPrivate::ModifiedRow mr = d->cache.value(akey: row);
1468 if (mr.op() != QSqlTableModelPrivate::None)
1469 return mr.primaryValues(pi: pIndex);
1470 else
1471 return QSqlQueryModel::record(row).keyValues(keyFields: pIndex);
1472}
1473
1474QT_END_NAMESPACE
1475

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