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

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

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 40.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtSql module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department 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 return rec.indexOf(name);
102}
103
104void QSqlTableModelPrivate::initRecordAndPrimaryIndex()
105{
106 rec = db.record(tableName);
107 primaryIndex = db.primaryIndex(tableName);
108}
109
110void QSqlTableModelPrivate::clear()
111{
112 editIndex = -1;
113 sortColumn = -1;
114 sortOrder = Qt::AscendingOrder;
115 tableName.clear();
116 editQuery.clear();
117 editBuffer.clear();
118 cache.clear();
119 primaryIndex.clear();
120 rec.clear();
121 filter.clear();
122}
123
124void QSqlTableModelPrivate::revertInsertedRow()
125{
126 Q_Q(QSqlTableModel);
127 if (insertIndex == -1)
128 return;
129
130 q->beginRemoveRows(QModelIndex(), insertIndex, insertIndex);
131 insertIndex = -1;
132 q->endRemoveRows();
133}
134
135void QSqlTableModelPrivate::clearEditBuffer()
136{
137 editBuffer = rec;
138}
139
140void QSqlTableModelPrivate::clearCache()
141{
142 cache.clear();
143}
144
145void QSqlTableModelPrivate::revertCachedRow(int row)
146{
147 Q_Q(QSqlTableModel);
148 ModifiedRow r = cache.value(row);
149 switch (r.op) {
150 case QSqlTableModelPrivate::None:
151 Q_ASSERT_X(false, "QSqlTableModelPrivate::revertCachedRow()", "Invalid entry in cache map");
152 return;
153 case QSqlTableModelPrivate::Update:
154 case QSqlTableModelPrivate::Delete:
155 cache.remove(row);
156 emit q->dataChanged(q->createIndex(row, 0),
157 q->createIndex(row, q->columnCount() - 1));
158 break;
159 case QSqlTableModelPrivate::Insert: {
160 QMap<int, QSqlTableModelPrivate::ModifiedRow>::Iterator it = cache.find(row);
161 if (it == cache.end())
162 return;
163 q->beginRemoveRows(QModelIndex(), row, row);
164 it = cache.erase(it);
165 while (it != cache.end()) {
166 int oldKey = it.key();
167 const QSqlTableModelPrivate::ModifiedRow oldValue = it.value();
168 cache.erase(it);
169 it = cache.insert(oldKey - 1, oldValue);
170 ++it;
171 }
172 q->endRemoveRows();
173 break; }
174 }
175}
176
177bool QSqlTableModelPrivate::exec(const QString &stmt, bool prepStatement,
178 const QSqlRecord &rec, const QSqlRecord &whereValues)
179{
180 if (stmt.isEmpty())
181 return false;
182
183 // lazy initialization of editQuery
184 if (editQuery.driver() != db.driver())
185 editQuery = QSqlQuery(db);
186
187 // workaround for In-Process databases - remove all read locks
188 // from the table to make sure the editQuery succeeds
189 if (db.driver()->hasFeature(QSqlDriver::SimpleLocking))
190 const_cast<QSqlResult *>(query.result())->detachFromResultSet();
191
192 if (prepStatement) {
193 if (editQuery.lastQuery() != stmt) {
194 if (!editQuery.prepare(stmt)) {
195 error = editQuery.lastError();
196 return false;
197 }
198 }
199 int i;
200 for (i = 0; i < rec.count(); ++i) {
201 if (rec.isGenerated(i) && rec.value(i).type() != QVariant::Invalid)
202 editQuery.addBindValue(rec.value(i));
203 }
204 for (i = 0; i < whereValues.count(); ++i) {
205 if (whereValues.isGenerated(i))
206 editQuery.addBindValue(whereValues.value(i));
207 }
208
209 if (!editQuery.exec()) {
210 error = editQuery.lastError();
211 return false;
212 }
213 } else {
214 if (!editQuery.exec(stmt)) {
215 error = editQuery.lastError();
216 return false;
217 }
218 }
219 return true;
220}
221
222QSqlRecord QSqlTableModelPrivate::primaryValues(int row)
223{
224 QSqlRecord record;
225 if (!query.seek(row)) {
226 error = query.lastError();
227 return record;
228 }
229 if (primaryIndex.isEmpty()) {
230 record = rec;
231 for (int i = 0; i < record.count(); ++i)
232 record.setValue(i, query.value(i));
233 } else {
234 record = primaryIndex;
235 for (int i = 0; i < record.count(); ++i)
236 record.setValue(i, query.value(rec.indexOf(record.fieldName(i))));
237 }
238 return record;
239}
240
241/*!
242 \class QSqlTableModel
243 \brief The QSqlTableModel class provides an editable data model
244 for a single database table.
245
246 \ingroup database
247 \inmodule QtSql
248
249 QSqlTableModel is a high-level interface for reading and writing
250 database records from a single table. It is build on top of the
251 lower-level QSqlQuery and can be used to provide data to view
252 classes such as QTableView. For example:
253
254 \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 24
255
256 We set the SQL table's name and the edit strategy, then we set up
257 the labels displayed in the view header. The edit strategy
258 dictates when the changes done by the user in the view are
259 actually applied to the database. The possible values are \l
260 OnFieldChange, \l OnRowChange, and \l OnManualSubmit.
261
262 QSqlTableModel can also be used to access a database
263 programmatically, without binding it to a view:
264
265 \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 25
266
267 The code snippet above extracts the \c salary field from record 4 in
268 the result set of the query \c{SELECT * from employee}.
269
270 It is possible to set filters using setFilter(), or modify the
271 sort order using setSort(). At the end, you must call select() to
272 populate the model with data.
273
274 The \l{sql/tablemodel} example illustrates how to use
275 QSqlTableModel as the data source for a QTableView.
276
277 QSqlTableModel provides no direct support for foreign keys. Use
278 the QSqlRelationalTableModel and QSqlRelationalDelegate if you
279 want to resolve foreign keys.
280
281 \sa QSqlRelationalTableModel, QSqlQuery, {Model/View Programming},
282 {Table Model Example}, {Cached Table Example}
283*/
284
285/*!
286 \fn QSqlTableModel::beforeDelete(int row)
287
288 This signal is emitted by deleteRowFromTable() before the \a row
289 is deleted from the currently active database table.
290*/
291
292/*!
293 \fn void QSqlTableModel::primeInsert(int row, QSqlRecord &record)
294
295 This signal is emitted by insertRows(), when an insertion is
296 initiated in the given \a row of the currently active database
297 table. The \a record parameter can be written to (since it is a
298 reference), for example to populate some fields with default
299 values.
300*/
301
302/*!
303 \fn QSqlTableModel::beforeInsert(QSqlRecord &record)
304
305 This signal is emitted by insertRowIntoTable() before a new row is
306 inserted into the currently active database table. The values that
307 are about to be inserted are stored in \a record and can be
308 modified before they will be inserted.
309*/
310
311/*!
312 \fn QSqlTableModel::beforeUpdate(int row, QSqlRecord &record)
313
314 This signal is emitted by updateRowInTable() before the \a row is
315 updated in the currently active database table with the values
316 from \a record.
317
318 Note that only values that are marked as generated will be updated.
319 The generated flag can be set with \l QSqlRecord::setGenerated()
320 and checked with \l QSqlRecord::isGenerated().
321
322 \sa QSqlRecord::isGenerated()
323*/
324
325/*!
326 Creates an empty QSqlTableModel and sets the parent to \a parent
327 and the database connection to \a db. If \a db is not valid, the
328 default database connection will be used.
329
330 The default edit strategy is \l OnRowChange.
331*/
332QSqlTableModel::QSqlTableModel(QObject *parent, QSqlDatabase db)
333 : QSqlQueryModel(*new QSqlTableModelPrivate, parent)
334{
335 Q_D(QSqlTableModel);
336 d->db = db.isValid() ? db : QSqlDatabase::database();
337}
338
339/*! \internal
340*/
341QSqlTableModel::QSqlTableModel(QSqlTableModelPrivate &dd, QObject *parent, QSqlDatabase db)
342 : QSqlQueryModel(dd, parent)
343{
344 Q_D(QSqlTableModel);
345 d->db = db.isValid() ? db : QSqlDatabase::database();
346}
347
348/*!
349 Destroys the object and frees any allocated resources.
350*/
351QSqlTableModel::~QSqlTableModel()
352{
353}
354
355/*!
356 Sets the database table on which the model operates to \a
357 tableName. Does not select data from the table, but fetches its
358 field information.
359
360 To populate the model with the table's data, call select().
361
362 Error information can be retrieved with \l lastError().
363
364 \sa select(), setFilter(), lastError()
365*/
366void QSqlTableModel::setTable(const QString &tableName)
367{
368 Q_D(QSqlTableModel);
369 clear();
370 if(d->db.tables().contains(tableName.toUpper()))
371 d->tableName = tableName.toUpper();
372 else
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()) {
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/*!