1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the sql module of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
29
#include "qsqlquerymodel.h"
31
#include <qsqldriver.h>
32
#include <qsqlfield.h>
34
#include "qsqlquerymodel_p.h"
36
#define QSQL_PREFETCH 255
38
void QSqlQueryModelPrivate::prefetch(int limit)
42
if (atEnd || limit <= bottom.row())
45
QModelIndex oldBottom = q->createIndex(bottom.row(), 0);
46
QModelIndex newBottom;
48
// try to seek directly
49
if (query.seek(limit)) {
50
newBottom = q->createIndex(limit, bottom.column());
52
// fetch as far as we can
54
newBottom = q->createIndex(query.at(), bottom.column());
56
error = query.lastError();
57
newBottom = q->createIndex(-1, bottom.column());
59
atEnd = true; // this is the end.
61
if (newBottom.row() > oldBottom.row()) {
62
emit q->beginInsertRows(QModelIndex(), oldBottom.row(), newBottom.row());
64
emit q->endInsertRows();
70
QSqlQueryModelPrivate::~QSqlQueryModelPrivate()
76
\brief The QSqlQueryModel class provides a read-only data model for SQL
82
QSqlQueryModel is a high-level interface for executing SQL
83
statements and traversing the result set. It is built on top of
84
the lower-level QSqlQuery and can be used to provide data to
85
view classes such as QTableView. For example:
87
\quotefromfile snippets/sqldatabase/sqldatabase.cpp
88
\skipto QSqlQueryModel_snippets
89
\skipto QSqlQueryModel *model
92
We set the model's query, then we set up the labels displayed in
95
QSqlQueryModel can also be used to access a database
96
programmatically, without binding it to a view:
98
\skipto QSqlQueryModel model;
99
\printuntil int salary =
101
The code snippet above extracts the \c salary field from record 4 in
102
the result set of the query \c{SELECT * from employee}. Assuming
103
that \c salary is column 2, we can rewrite the last line as follows:
106
\printline int salary =
108
The model is read-only by default. To make it read-write, you
109
must subclass it and reimplement setData() and flags(). Another
110
option is to use QSqlTableModel, which provides a read-write
111
model based on a single database table.
113
The \l{sql/querymodel} example illustrates how to use
114
QSqlQueryModel to display the result of a query. It also shows
115
how to subclass QSqlQueryModel to customize the contents of the
116
data before showing it to the user, and how to create a
117
read-write model based on QSqlQueryModel.
119
\sa QSqlTableModel, QSqlRelationalTableModel, QSqlQuery,
120
{Model/View Programming}
124
Creates an empty QSqlQueryModel with the given \a parent.
126
QSqlQueryModel::QSqlQueryModel(QObject *parent)
127
: QAbstractTableModel(*new QSqlQueryModelPrivate, parent)
133
QSqlQueryModel::QSqlQueryModel(QSqlQueryModelPrivate &dd, QObject *parent)
134
: QAbstractTableModel(dd, parent)
139
Destroys the object and frees any allocated resources.
143
QSqlQueryModel::~QSqlQueryModel()
150
void QSqlQueryModel::fetchMore(const QModelIndex &parent)
153
if (parent.isValid())
155
d->prefetch(d->bottom.row() + QSQL_PREFETCH);
160
bool QSqlQueryModel::canFetchMore(const QModelIndex &parent) const
162
Q_D(const QSqlQueryModel);
163
return (!parent.isValid() && !d->atEnd);
168
int QSqlQueryModel::rowCount(const QModelIndex &) const
170
Q_D(const QSqlQueryModel);
171
return d->bottom.row() + 1;
176
int QSqlQueryModel::columnCount(const QModelIndex &) const
178
Q_D(const QSqlQueryModel);
179
return d->rec.count();
183
Returns the value for the specified \a item and \a role.
185
If \a item is out of bounds or if an error occurred, an invalid
186
QVariant is returned.
190
QVariant QSqlQueryModel::data(const QModelIndex &item, int role) const
192
Q_D(const QSqlQueryModel);
197
if (role & ~(Qt::DisplayRole | Qt::EditRole))
200
if (!d->rec.isGenerated(item.column()))
202
QModelIndex dItem = indexInQuery(item);
203
if (dItem.row() > d->bottom.row())
204
const_cast<QSqlQueryModelPrivate *>(d)->prefetch(dItem.row());
206
if (!d->query.seek(dItem.row())) {
207
d->error = d->query.lastError();
211
return d->query.value(dItem.column());
215
Returns the header data for the given \a role in the \a section
216
of the header with the specified \a orientation.
218
QVariant QSqlQueryModel::headerData(int section, Qt::Orientation orientation, int role) const
220
Q_D(const QSqlQueryModel);
221
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
222
QVariant val = d->headers.value(section);
225
if (d->rec.count() > section)
226
return d->rec.fieldName(section);
228
return QAbstractItemModel::headerData(section, orientation, role);
232
This virtual function is called whenever the query changes. The
233
default implementation does nothing.
235
query() returns the new query.
237
\sa query(), setQuery()
239
void QSqlQueryModel::queryChange()
245
Resets the model and sets the data provider to be the given \a
246
query. Note that the query must be active and must not be
249
\sa query(), QSqlQuery::isActive(), QSqlQuery::setForwardOnly()
251
void QSqlQueryModel::setQuery(const QSqlQuery &query)
254
QSqlRecord newRec = query.record();
255
bool columnsChanged = (newRec != d->rec);
256
bool hasQuerySize = d->query.driver()->hasFeature(QSqlDriver::QuerySize);
258
if (d->colOffsets.size() != newRec.count() || columnsChanged) {
259
d->colOffsets.resize(newRec.count());
260
memset(d->colOffsets.data(), 0, d->colOffsets.size() * sizeof(int));
263
beginRemoveRows(QModelIndex(), 0, d->bottom.row());
265
d->bottom = QModelIndex();
266
d->error = QSqlError();
276
if (!query.isActive() || query.isForwardOnly()) {
278
d->bottom = QModelIndex();
279
if (query.isForwardOnly())
280
d->error = QSqlError(QLatin1String("Forward-only queries "
281
"cannot be used in a data model"),
282
QString(), QSqlError::ConnectionError);
284
d->error = query.lastError();
287
QModelIndex newBottom;
289
beginInsertRows(QModelIndex(), 0, newBottom.row());
290
newBottom = createIndex(d->query.size() - 1, d->rec.count() - 1);
291
d->bottom = createIndex(d->query.size() - 1, columnsChanged ? 0 : d->rec.count() - 1);
295
newBottom = createIndex(0, d->rec.count() - 1);
297
if (columnsChanged) {
298
beginInsertColumns(QModelIndex(), 0, newBottom.column());
299
d->bottom = newBottom;
302
d->bottom = newBottom;
307
// fetchMore does the rowsInserted stuff for incremental models
313
Executes the query \a query for the given database connection \a
314
db. If no database is specified, the default connection is used.
316
\sa query(), queryChange()
318
void QSqlQueryModel::setQuery(const QString &query, const QSqlDatabase &db)
320
setQuery(QSqlQuery(query, db));
324
Clears the model and releases any aquired resource.
326
void QSqlQueryModel::clear()
329
d->error = QSqlError();
333
d->colOffsets.clear();
334
d->bottom = QModelIndex();
339
Sets the caption for the header with the given \a orientation to
340
the specified \a value. This is useful if the model is used to
341
display data in a view (e.g., QTableView).
343
Returns true if \a role is \c Qt::DisplayRole and
344
the \a section refers to a valid section; otherwise returns
347
Note that this function cannot be used to modify values in the
348
database since the model is read-only.
352
bool QSqlQueryModel::setHeaderData(int section, Qt::Orientation orientation,
353
const QVariant &value, int role)
356
if ((role != Qt::EditRole && role != Qt::DisplayRole)
357
|| orientation != Qt::Horizontal || section < 0)
360
if (d->headers.size() <= section)
361
d->headers.resize(qMax(section + 1, 16));
362
d->headers[section] = value;
367
Returns the QSqlQuery associated with this model.
371
QSqlQuery QSqlQueryModel::query() const
373
Q_D(const QSqlQueryModel);
378
Returns information about the last error that occurred on the
381
QSqlError QSqlQueryModel::lastError() const
383
Q_D(const QSqlQueryModel);
388
Protected function which allows derived classes to set the value of
389
the last error that occurred on the database to \a error.
393
void QSqlQueryModel::setLastError(const QSqlError &error)
400
Returns the record containing information about the fields of the
401
current query. If \a row is the index of a valid row, the record
402
will be populated with values from that row.
404
If the model is not initialized, an empty record will be
407
\sa QSqlRecord::isEmpty()
409
QSqlRecord QSqlQueryModel::record(int row) const
411
Q_D(const QSqlQueryModel);
415
QSqlRecord rec = d->rec;
416
for (int i = 0; i < rec.count(); ++i)
417
rec.setValue(i, data(createIndex(row, i), Qt::EditRole));
423
Returns an empty record containing information about the fields
424
of the current query.
426
If the model is not initialized, an empty record will be
429
\sa QSqlRecord::isEmpty()
431
QSqlRecord QSqlQueryModel::record() const
433
Q_D(const QSqlQueryModel);
438
Inserts \a count columns into the model at position \a column. The
439
\a parent parameter must always be an invalid QModelIndex, since
440
the model does not support parent-child relationships.
442
Returns true if \a column is within bounds; otherwise returns false.
444
By default, inserted columns are empty. To fill them with data,
445
reimplement data() and handle any inserted column separately:
447
\quotefromfile snippets/sqldatabase/sqldatabase.cpp
448
\skipto QSqlQueryModel_snippets
449
\skipto MyModel::data(
454
bool QSqlQueryModel::insertColumns(int column, int count, const QModelIndex &parent)
457
if (count <= 0 || parent.isValid() || column < 0 || column > d->rec.count())
460
beginInsertColumns(parent, column, column + count - 1);
461
for (int c = 0; c < count; ++c) {
463
field.setReadOnly(true);
464
field.setGenerated(false);
465
d->rec.insert(column, field);
466
if (d->colOffsets.size() < d->rec.count()) {
467
int nVal = d->colOffsets.isEmpty() ? 0 : d->colOffsets[d->colOffsets.size() - 1];
468
d->colOffsets.append(nVal);
469
Q_ASSERT(d->colOffsets.size() >= d->rec.count());
471
for (int i = column + 1; i < d->colOffsets.count(); ++i)
479
Removes \a count columns from the model starting from position \a
480
column. The \a parent parameter must always be an invalid
481
QModelIndex, since the model does not support parent-child
484
Removing columns effectively hides them. It does not affect the
485
underlying QSqlQuery.
487
Returns true if the columns were removed; otherwise returns false.
489
bool QSqlQueryModel::removeColumns(int column, int count, const QModelIndex &parent)
492
if (count <= 0 || parent.isValid() || column < 0 || column >= d->rec.count())
495
beginRemoveColumns(parent, column, column + count - 1);
498
for (i = 0; i < count; ++i)
499
d->rec.remove(column);
500
for (i = column; i < d->colOffsets.count(); ++i)
501
d->colOffsets[i] -= count;
508
Returns the index of the value in the database result set for the
509
given \a item in the model.
511
The return value is identical to \a item if no columns or rows
512
have been inserted, removed, or moved around.
514
Returns an invalid model index if \a item is out of bounds or if
515
\a item does not point to a value in the result set.
517
\sa QSqlTableModel::indexInQuery(), insertColumns(), removeColumns()
519
QModelIndex QSqlQueryModel::indexInQuery(const QModelIndex &item) const
521
Q_D(const QSqlQueryModel);
522
if (item.column() < 0 || item.column() >= d->rec.count()
523
|| !d->rec.isGenerated(item.column()))
524
return QModelIndex();
525
return createIndex(item.row(), item.column() - d->colOffsets[item.column()],
526
item.internalPointer());