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