1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtSql module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qsqlquerymodel.h"
45 #include <qsqldriver.h>
46 #include <qsqlfield.h>
48 #include "qsqlquerymodel_p.h"
52 #define QSQL_PREFETCH 255
54 void QSqlQueryModelPrivate::prefetch(int limit)
58 if (atEnd || limit <= bottom.row() || bottom.column() == -1)
61 QModelIndex newBottom;
62 const int oldBottomRow = qMax(bottom.row(), 0);
64 // try to seek directly
65 if (query.seek(limit)) {
66 newBottom = q->createIndex(limit, bottom.column());
68 // have to seek back to our old position for MS Access
73 newBottom = q->createIndex(i, bottom.column());
75 // empty or invalid query
76 newBottom = q->createIndex(-1, bottom.column());
78 atEnd = true; // this is the end.
80 if (newBottom.row() >= 0 && newBottom.row() > bottom.row()) {
81 q->beginInsertRows(QModelIndex(), bottom.row() + 1, newBottom.row());
89 QSqlQueryModelPrivate::~QSqlQueryModelPrivate()
93 void QSqlQueryModelPrivate::initColOffsets(int size)
95 colOffsets.resize(size);
96 memset(colOffsets.data(), 0, colOffsets.size() * sizeof(int));
100 \class QSqlQueryModel
101 \brief The QSqlQueryModel class provides a read-only data model for SQL
107 QSqlQueryModel is a high-level interface for executing SQL
108 statements and traversing the result set. It is built on top of
109 the lower-level QSqlQuery and can be used to provide data to
110 view classes such as QTableView. For example:
112 \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 16
114 We set the model's query, then we set up the labels displayed in
117 QSqlQueryModel can also be used to access a database
118 programmatically, without binding it to a view:
120 \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 21
122 The code snippet above extracts the \c salary field from record 4 in
123 the result set of the query \c{SELECT * from employee}. Assuming
124 that \c salary is column 2, we can rewrite the last line as follows:
126 \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 22
128 The model is read-only by default. To make it read-write, you
129 must subclass it and reimplement setData() and flags(). Another
130 option is to use QSqlTableModel, which provides a read-write
131 model based on a single database table.
133 The \l{sql/querymodel} example illustrates how to use
134 QSqlQueryModel to display the result of a query. It also shows
135 how to subclass QSqlQueryModel to customize the contents of the
136 data before showing it to the user, and how to create a
137 read-write model based on QSqlQueryModel.
139 If the database doesn't return the amount of selected rows in
140 a query, the model will fetch rows incrementally.
141 See fetchMore() for more information.
143 \sa QSqlTableModel, QSqlRelationalTableModel, QSqlQuery,
144 {Model/View Programming}, {Query Model Example}
148 Creates an empty QSqlQueryModel with the given \a parent.
150 QSqlQueryModel::QSqlQueryModel(QObject *parent)
151 : QAbstractTableModel(*new QSqlQueryModelPrivate, parent)
157 QSqlQueryModel::QSqlQueryModel(QSqlQueryModelPrivate &dd, QObject *parent)
158 : QAbstractTableModel(dd, parent)
163 Destroys the object and frees any allocated resources.
167 QSqlQueryModel::~QSqlQueryModel()
174 Fetches more rows from a database.
175 This only affects databases that don't report back the size of a query
176 (see QSqlDriver::hasFeature()).
178 To force fetching of the entire database, you can use the following:
180 \snippet doc/src/snippets/code/src_sql_models_qsqlquerymodel.cpp 0
182 \a parent should always be an invalid QModelIndex.
186 void QSqlQueryModel::fetchMore(const QModelIndex &parent)
189 if (parent.isValid())
191 d->prefetch(qMax(d->bottom.row(), 0) + QSQL_PREFETCH);
197 Returns true if it is possible to read more rows from the database.
198 This only affects databases that don't report back the size of a query
199 (see QSqlDriver::hasFeature()).
201 \a parent should always be an invalid QModelIndex.
205 bool QSqlQueryModel::canFetchMore(const QModelIndex &parent) const
207 Q_D(const QSqlQueryModel);
208 return (!parent.isValid() && !d->atEnd);
211 /*! \fn int QSqlQueryModel::rowCount(const QModelIndex &parent) const
214 If the database supports returning the size of a query
215 (see QSqlDriver::hasFeature()), the amount of rows of the current
216 query is returned. Otherwise, returns the amount of rows
217 currently cached on the client.
219 \a parent should always be an invalid QModelIndex.
221 \sa canFetchMore(), QSqlDriver::hasFeature()
223 int QSqlQueryModel::rowCount(const QModelIndex &index) const
225 Q_D(const QSqlQueryModel);
226 return index.isValid() ? 0 : d->bottom.row() + 1;
231 int QSqlQueryModel::columnCount(const QModelIndex &index) const
233 Q_D(const QSqlQueryModel);
234 return index.isValid() ? 0 : d->rec.count();
238 Returns the value for the specified \a item and \a role.
240 If \a item is out of bounds or if an error occurred, an invalid
241 QVariant is returned.
245 QVariant QSqlQueryModel::data(const QModelIndex &item, int role) const
247 Q_D(const QSqlQueryModel);
252 if (role & ~(Qt::DisplayRole | Qt::EditRole))
255 if (!d->rec.isGenerated(item.column()))
257 QModelIndex dItem = indexInQuery(item);
258 if (dItem.row() > d->bottom.row())
259 const_cast<QSqlQueryModelPrivate *>(d)->prefetch(dItem.row());
261 if (!d->query.seek(dItem.row())) {
262 d->error = d->query.lastError();
266 return d->query.value(dItem.column());
270 Returns the header data for the given \a role in the \a section
271 of the header with the specified \a orientation.
273 QVariant QSqlQueryModel::headerData(int section, Qt::Orientation orientation, int role) const
275 Q_D(const QSqlQueryModel);
276 if (orientation == Qt::Horizontal) {
277 QVariant val = d->headers.value(section).value(role);
278 if (role == Qt::DisplayRole && !val.isValid())
279 val = d->headers.value(section).value(Qt::EditRole);
283 // See if it's an inserted column (iiq.column() != -1)
284 QModelIndex dItem = indexInQuery(createIndex(0, section));
286 if (role == Qt::DisplayRole && d->rec.count() > section && dItem.column() != -1)
287 return d->rec.fieldName(section);
289 return QAbstractItemModel::headerData(section, orientation, role);
293 This virtual function is called whenever the query changes. The
294 default implementation does nothing.
296 query() returns the new query.
298 \sa query(), setQuery()
300 void QSqlQueryModel::queryChange()
306 Resets the model and sets the data provider to be the given \a
307 query. Note that the query must be active and must not be
310 lastError() can be used to retrieve verbose information if there
311 was an error setting the query.
313 \note Calling setQuery() will remove any inserted columns.
315 \sa query(), QSqlQuery::isActive(), QSqlQuery::setForwardOnly(), lastError()
317 void QSqlQueryModel::setQuery(const QSqlQuery &query)
320 QSqlRecord newRec = query.record();
321 bool columnsChanged = (newRec != d->rec);
322 bool hasQuerySize = query.driver()->hasFeature(QSqlDriver::QuerySize);
323 bool hasNewData = (newRec != QSqlRecord()) || !query.lastError().isValid();
325 if (d->colOffsets.size() != newRec.count() || columnsChanged)
326 d->initColOffsets(newRec.count());
328 bool mustClearModel = d->bottom.isValid();
329 if (mustClearModel) {
331 beginRemoveRows(QModelIndex(), 0, qMax(d->bottom.row(), 0));
332 d->bottom = QModelIndex();
335 d->error = QSqlError();
344 if (columnsChanged && hasNewData)
347 if (!query.isActive() || query.isForwardOnly()) {
349 d->bottom = QModelIndex();
350 if (query.isForwardOnly())
351 d->error = QSqlError(QLatin1String("Forward-only queries "
352 "cannot be used in a data model"),
353 QString(), QSqlError::ConnectionError);
355 d->error = query.lastError();
358 QModelIndex newBottom;
359 if (hasQuerySize && d->query.size() > 0) {
360 newBottom = createIndex(d->query.size() - 1, d->rec.count() - 1);
361 beginInsertRows(QModelIndex(), 0, qMax(0, newBottom.row()));
362 d->bottom = createIndex(d->query.size() - 1, columnsChanged ? 0 : d->rec.count() - 1);
366 newBottom = createIndex(-1, d->rec.count() - 1);
368 d->bottom = newBottom;
372 // fetchMore does the rowsInserted stuff for incremental models
378 Executes the query \a query for the given database connection \a
379 db. If no database is specified, the default connection is used.
381 lastError() can be used to retrieve verbose information if there
382 was an error setting the query.
385 \snippet doc/src/snippets/code/src_sql_models_qsqlquerymodel.cpp 1
387 \sa query(), queryChange(), lastError()
389 void QSqlQueryModel::setQuery(const QString &query, const QSqlDatabase &db)
391 setQuery(QSqlQuery(query, db));
395 Clears the model and releases any acquired resource.
397 void QSqlQueryModel::clear()
400 d->error = QSqlError();
404 d->colOffsets.clear();
405 d->bottom = QModelIndex();
410 Sets the caption for a horizontal header for the specified \a role to
411 \a value. This is useful if the model is used to
412 display data in a view (e.g., QTableView).
414 Returns true if \a orientation is Qt::Horizontal and
415 the \a section refers to a valid section; otherwise returns
418 Note that this function cannot be used to modify values in the
419 database since the model is read-only.
423 bool QSqlQueryModel::setHeaderData(int section, Qt::Orientation orientation,
424 const QVariant &value, int role)
427 if (orientation != Qt::Horizontal || section < 0 || columnCount() <= section)
430 if (d->headers.size() <= section)
431 d->headers.resize(qMax(section + 1, 16));
432 d->headers[section][role] = value;
433 emit headerDataChanged(orientation, section, section);
438 Returns the QSqlQuery associated with this model.
442 QSqlQuery QSqlQueryModel::query() const
444 Q_D(const QSqlQueryModel);
449 Returns information about the last error that occurred on the
454 QSqlError QSqlQueryModel::lastError() const
456 Q_D(const QSqlQueryModel);
461 Protected function which allows derived classes to set the value of
462 the last error that occurred on the database to \a error.
466 void QSqlQueryModel::setLastError(const QSqlError &error)
473 Returns the record containing information about the fields of the
474 current query. If \a row is the index of a valid row, the record
475 will be populated with values from that row.
477 If the model is not initialized, an empty record will be
480 \sa QSqlRecord::isEmpty()
482 QSqlRecord QSqlQueryModel::record(int row) const
484 Q_D(const QSqlQueryModel);
488 QSqlRecord rec = d->rec;
489 for (int i = 0; i < rec.count(); ++i)
490 rec.setValue(i, data(createIndex(row, i), Qt::EditRole));
496 Returns an empty record containing information about the fields
497 of the current query.
499 If the model is not initialized, an empty record will be
502 \sa QSqlRecord::isEmpty()
504 QSqlRecord QSqlQueryModel::record() const
506 Q_D(const QSqlQueryModel);
511 Inserts \a count columns into the model at position \a column. The
512 \a parent parameter must always be an invalid QModelIndex, since
513 the model does not support parent-child relationships.
515 Returns true if \a column is within bounds; otherwise returns false.
517 By default, inserted columns are empty. To fill them with data,
518 reimplement data() and handle any inserted column separately:
520 \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 23
524 bool QSqlQueryModel::insertColumns(int column, int count, const QModelIndex &parent)
527 if (count <= 0 || parent.isValid() || column < 0 || column > d->rec.count())
530 beginInsertColumns(parent, column, column + count - 1);
531 for (int c = 0; c < count; ++c) {
533 field.setReadOnly(true);
534 field.setGenerated(false);
535 d->rec.insert(column, field);
536 if (d->colOffsets.size() < d->rec.count()) {
537 int nVal = d->colOffsets.isEmpty() ? 0 : d->colOffsets[d->colOffsets.size() - 1];
538 d->colOffsets.append(nVal);
539 Q_ASSERT(d->colOffsets.size() >= d->rec.count());
541 for (int i = column + 1; i < d->colOffsets.count(); ++i)
549 Removes \a count columns from the model starting from position \a
550 column. The \a parent parameter must always be an invalid
551 QModelIndex, since the model does not support parent-child
554 Removing columns effectively hides them. It does not affect the
555 underlying QSqlQuery.
557 Returns true if the columns were removed; otherwise returns false.
559 bool QSqlQueryModel::removeColumns(int column, int count, const QModelIndex &parent)
562 if (count <= 0 || parent.isValid() || column < 0 || column >= d->rec.count())
565 beginRemoveColumns(parent, column, column + count - 1);
568 for (i = 0; i < count; ++i)
569 d->rec.remove(column);
570 for (i = column; i < d->colOffsets.count(); ++i)
571 d->colOffsets[i] -= count;
578 Returns the index of the value in the database result set for the
579 given \a item in the model.
581 The return value is identical to \a item if no columns or rows
582 have been inserted, removed, or moved around.
584 Returns an invalid model index if \a item is out of bounds or if
585 \a item does not point to a value in the result set.
587 \sa QSqlTableModel::indexInQuery(), insertColumns(), removeColumns()
589 QModelIndex QSqlQueryModel::indexInQuery(const QModelIndex &item) const
591 Q_D(const QSqlQueryModel);
592 if (item.column() < 0 || item.column() >= d->rec.count()
593 || !d->rec.isGenerated(item.column()))
594 return QModelIndex();
595 return createIndex(item.row(), item.column() - d->colOffsets[item.column()],
596 item.internalPointer());