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

Last change on this file since 630 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 41.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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