source: trunk/src/sql/models/qsqltablemodel.cpp@ 754

Last change on this file since 754 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

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