src/sql/models/qsqlquerymodel.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtSql module of the Qt Toolkit.
       
     8 **
       
     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
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qsqlquerymodel.h"
       
    43 
       
    44 #include <qdebug.h>
       
    45 #include <qsqldriver.h>
       
    46 #include <qsqlfield.h>
       
    47 
       
    48 #include "qsqlquerymodel_p.h"
       
    49 
       
    50 QT_BEGIN_NAMESPACE
       
    51 
       
    52 #define QSQL_PREFETCH 255
       
    53 
       
    54 void QSqlQueryModelPrivate::prefetch(int limit)
       
    55 {
       
    56     Q_Q(QSqlQueryModel);
       
    57 
       
    58     if (atEnd || limit <= bottom.row() || bottom.column() == -1)
       
    59         return;
       
    60 
       
    61     QModelIndex newBottom;
       
    62     const int oldBottomRow = qMax(bottom.row(), 0);
       
    63 
       
    64     // try to seek directly
       
    65     if (query.seek(limit)) {
       
    66         newBottom = q->createIndex(limit, bottom.column());
       
    67     } else {
       
    68         // have to seek back to our old position for MS Access
       
    69         int i = oldBottomRow;
       
    70         if (query.seek(i)) {
       
    71             while (query.next())
       
    72                 ++i;
       
    73             newBottom = q->createIndex(i, bottom.column());
       
    74         } else {
       
    75             // empty or invalid query
       
    76             newBottom = q->createIndex(-1, bottom.column());
       
    77         }
       
    78         atEnd = true; // this is the end.
       
    79     }
       
    80     if (newBottom.row() >= 0 && newBottom.row() > bottom.row()) {
       
    81         q->beginInsertRows(QModelIndex(), bottom.row() + 1, newBottom.row());
       
    82         bottom = newBottom;
       
    83         q->endInsertRows();
       
    84     } else {
       
    85         bottom = newBottom;
       
    86     }
       
    87 }
       
    88 
       
    89 QSqlQueryModelPrivate::~QSqlQueryModelPrivate()
       
    90 {
       
    91 }
       
    92 
       
    93 void QSqlQueryModelPrivate::initColOffsets(int size)
       
    94 {
       
    95     colOffsets.resize(size);
       
    96     memset(colOffsets.data(), 0, colOffsets.size() * sizeof(int));
       
    97 }
       
    98 
       
    99 /*!
       
   100     \class QSqlQueryModel
       
   101     \brief The QSqlQueryModel class provides a read-only data model for SQL
       
   102     result sets.
       
   103 
       
   104     \ingroup database
       
   105     \inmodule QtSql
       
   106 
       
   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:
       
   111 
       
   112     \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 16
       
   113 
       
   114     We set the model's query, then we set up the labels displayed in
       
   115     the view header.
       
   116 
       
   117     QSqlQueryModel can also be used to access a database
       
   118     programmatically, without binding it to a view:
       
   119 
       
   120     \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 21
       
   121 
       
   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:
       
   125 
       
   126     \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 22
       
   127 
       
   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.
       
   132 
       
   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.
       
   138 
       
   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.
       
   142 
       
   143     \sa QSqlTableModel, QSqlRelationalTableModel, QSqlQuery,
       
   144         {Model/View Programming}, {Query Model Example}
       
   145 */
       
   146 
       
   147 /*!
       
   148     Creates an empty QSqlQueryModel with the given \a parent.
       
   149  */
       
   150 QSqlQueryModel::QSqlQueryModel(QObject *parent)
       
   151     : QAbstractTableModel(*new QSqlQueryModelPrivate, parent)
       
   152 {
       
   153 }
       
   154 
       
   155 /*! \internal
       
   156  */
       
   157 QSqlQueryModel::QSqlQueryModel(QSqlQueryModelPrivate &dd, QObject *parent)
       
   158     : QAbstractTableModel(dd, parent)
       
   159 {
       
   160 }
       
   161 
       
   162 /*!
       
   163     Destroys the object and frees any allocated resources.
       
   164 
       
   165     \sa clear()
       
   166 */
       
   167 QSqlQueryModel::~QSqlQueryModel()
       
   168 {
       
   169 }
       
   170 
       
   171 /*!
       
   172     \since 4.1
       
   173 
       
   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()).
       
   177 
       
   178     To force fetching of the entire database, you can use the following:
       
   179 
       
   180     \snippet doc/src/snippets/code/src_sql_models_qsqlquerymodel.cpp 0
       
   181 
       
   182     \a parent should always be an invalid QModelIndex.
       
   183 
       
   184     \sa canFetchMore()
       
   185 */
       
   186 void QSqlQueryModel::fetchMore(const QModelIndex &parent)
       
   187 {
       
   188     Q_D(QSqlQueryModel);
       
   189     if (parent.isValid())
       
   190         return;
       
   191     d->prefetch(qMax(d->bottom.row(), 0) + QSQL_PREFETCH);
       
   192 }
       
   193 
       
   194 /*!
       
   195     \since 4.1
       
   196 
       
   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()).
       
   200 
       
   201     \a parent should always be an invalid QModelIndex.
       
   202 
       
   203     \sa fetchMore()
       
   204  */
       
   205 bool QSqlQueryModel::canFetchMore(const QModelIndex &parent) const
       
   206 {
       
   207     Q_D(const QSqlQueryModel);
       
   208     return (!parent.isValid() && !d->atEnd);
       
   209 }
       
   210 
       
   211 /*! \fn int QSqlQueryModel::rowCount(const QModelIndex &parent) const
       
   212     \since 4.1
       
   213 
       
   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.
       
   218 
       
   219     \a parent should always be an invalid QModelIndex.
       
   220 
       
   221     \sa canFetchMore(), QSqlDriver::hasFeature()
       
   222  */
       
   223 int QSqlQueryModel::rowCount(const QModelIndex &index) const
       
   224 {
       
   225     Q_D(const QSqlQueryModel);
       
   226     return index.isValid() ? 0 : d->bottom.row() + 1;
       
   227 }
       
   228 
       
   229 /*! \reimp
       
   230  */
       
   231 int QSqlQueryModel::columnCount(const QModelIndex &index) const
       
   232 {
       
   233     Q_D(const QSqlQueryModel);
       
   234     return index.isValid() ? 0 : d->rec.count();
       
   235 }
       
   236 
       
   237 /*!
       
   238     Returns the value for the specified \a item and \a role.
       
   239 
       
   240     If \a item is out of bounds or if an error occurred, an invalid
       
   241     QVariant is returned.
       
   242 
       
   243     \sa lastError()
       
   244 */
       
   245 QVariant QSqlQueryModel::data(const QModelIndex &item, int role) const
       
   246 {
       
   247     Q_D(const QSqlQueryModel);
       
   248     if (!item.isValid())
       
   249         return QVariant();
       
   250 
       
   251     QVariant v;
       
   252     if (role & ~(Qt::DisplayRole | Qt::EditRole))
       
   253         return v;
       
   254 
       
   255     if (!d->rec.isGenerated(item.column()))
       
   256         return v;
       
   257     QModelIndex dItem = indexInQuery(item);
       
   258     if (dItem.row() > d->bottom.row())
       
   259         const_cast<QSqlQueryModelPrivate *>(d)->prefetch(dItem.row());
       
   260 
       
   261     if (!d->query.seek(dItem.row())) {
       
   262         d->error = d->query.lastError();
       
   263         return v;
       
   264     }
       
   265 
       
   266     return d->query.value(dItem.column());
       
   267 }
       
   268 
       
   269 /*!
       
   270     Returns the header data for the given \a role in the \a section
       
   271     of the header with the specified \a orientation.
       
   272 */
       
   273 QVariant QSqlQueryModel::headerData(int section, Qt::Orientation orientation, int role) const
       
   274 {
       
   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);
       
   280         if (val.isValid())
       
   281             return val;
       
   282         if (role == Qt::DisplayRole && d->rec.count() > section)
       
   283             return d->rec.fieldName(section);
       
   284     }
       
   285     return QAbstractItemModel::headerData(section, orientation, role);
       
   286 }
       
   287 
       
   288 /*!
       
   289     This virtual function is called whenever the query changes. The
       
   290     default implementation does nothing.
       
   291 
       
   292     query() returns the new query.
       
   293 
       
   294     \sa query(), setQuery()
       
   295  */
       
   296 void QSqlQueryModel::queryChange()
       
   297 {
       
   298     // do nothing
       
   299 }
       
   300 
       
   301 /*!
       
   302     Resets the model and sets the data provider to be the given \a
       
   303     query. Note that the query must be active and must not be
       
   304     isForwardOnly().
       
   305 
       
   306     lastError() can be used to retrieve verbose information if there
       
   307     was an error setting the query.
       
   308 
       
   309     \sa query(), QSqlQuery::isActive(), QSqlQuery::setForwardOnly(), lastError()
       
   310 */
       
   311 void QSqlQueryModel::setQuery(const QSqlQuery &query)
       
   312 {
       
   313     Q_D(QSqlQueryModel);
       
   314     QSqlRecord newRec = query.record();
       
   315     bool columnsChanged = (newRec != d->rec);
       
   316     bool hasQuerySize = query.driver()->hasFeature(QSqlDriver::QuerySize);
       
   317 
       
   318     if (d->colOffsets.size() != newRec.count() || columnsChanged)
       
   319         d->initColOffsets(newRec.count());
       
   320 
       
   321     bool mustClearModel = d->bottom.isValid();
       
   322     if (mustClearModel) {
       
   323         d->atEnd = true;
       
   324         beginRemoveRows(QModelIndex(), 0, qMax(d->bottom.row(), 0));
       
   325         d->bottom = QModelIndex();
       
   326     }
       
   327 
       
   328     d->error = QSqlError();
       
   329     d->query = query;
       
   330     d->rec = newRec;
       
   331    
       
   332     if (mustClearModel)
       
   333         endRemoveRows();
       
   334     
       
   335     d->atEnd = false;    
       
   336 
       
   337     if (columnsChanged)
       
   338         reset();
       
   339 
       
   340     if (!query.isActive() || query.isForwardOnly()) {
       
   341         d->atEnd = true;
       
   342         d->bottom = QModelIndex();
       
   343         if (query.isForwardOnly())
       
   344             d->error = QSqlError(QLatin1String("Forward-only queries "
       
   345                                                "cannot be used in a data model"),
       
   346                                  QString(), QSqlError::ConnectionError);
       
   347         else
       
   348             d->error = query.lastError();
       
   349         return;
       
   350     }
       
   351     QModelIndex newBottom;
       
   352     if (hasQuerySize && d->query.size() > 0) {
       
   353         newBottom = createIndex(d->query.size() - 1, d->rec.count() - 1);
       
   354         beginInsertRows(QModelIndex(), 0, qMax(0, newBottom.row()));
       
   355         d->bottom = createIndex(d->query.size() - 1, columnsChanged ? 0 : d->rec.count() - 1);
       
   356         d->atEnd = true;
       
   357         endInsertRows();
       
   358     } else {
       
   359         newBottom = createIndex(-1, d->rec.count() - 1);
       
   360     }
       
   361     d->bottom = newBottom;
       
   362 
       
   363     queryChange();
       
   364 
       
   365     // fetchMore does the rowsInserted stuff for incremental models
       
   366     fetchMore();
       
   367 }
       
   368 
       
   369 /*! \overload
       
   370 
       
   371     Executes the query \a query for the given database connection \a
       
   372     db. If no database is specified, the default connection is used.
       
   373 
       
   374     lastError() can be used to retrieve verbose information if there
       
   375     was an error setting the query.
       
   376 
       
   377     Example:
       
   378     \snippet doc/src/snippets/code/src_sql_models_qsqlquerymodel.cpp 1
       
   379 
       
   380     \sa query(), queryChange(), lastError()
       
   381 */
       
   382 void QSqlQueryModel::setQuery(const QString &query, const QSqlDatabase &db)
       
   383 {
       
   384     setQuery(QSqlQuery(query, db));
       
   385 }
       
   386 
       
   387 /*!
       
   388     Clears the model and releases any acquired resource.
       
   389 */
       
   390 void QSqlQueryModel::clear()
       
   391 {
       
   392     Q_D(QSqlQueryModel);
       
   393     d->error = QSqlError();
       
   394     d->atEnd = true;
       
   395     d->query.clear();
       
   396     d->rec.clear();
       
   397     d->colOffsets.clear();
       
   398     d->bottom = QModelIndex();
       
   399     d->headers.clear();
       
   400 }
       
   401 
       
   402 /*!
       
   403     Sets the caption for a horizontal header for the specified \a role to
       
   404     \a value. This is useful if the model is used to
       
   405     display data in a view (e.g., QTableView).
       
   406 
       
   407     Returns true if \a orientation is Qt::Horizontal and
       
   408     the \a section refers to a valid section; otherwise returns
       
   409     false.
       
   410 
       
   411     Note that this function cannot be used to modify values in the
       
   412     database since the model is read-only.
       
   413 
       
   414     \sa data()
       
   415  */
       
   416 bool QSqlQueryModel::setHeaderData(int section, Qt::Orientation orientation,
       
   417                                    const QVariant &value, int role)
       
   418 {
       
   419     Q_D(QSqlQueryModel);
       
   420     if (orientation != Qt::Horizontal || section < 0)
       
   421         return false;
       
   422 
       
   423     if (d->headers.size() <= section)
       
   424         d->headers.resize(qMax(section + 1, 16));
       
   425     d->headers[section][role] = value;
       
   426     emit headerDataChanged(orientation, section, section);
       
   427     return true;
       
   428 }
       
   429 
       
   430 /*!
       
   431     Returns the QSqlQuery associated with this model.
       
   432 
       
   433     \sa setQuery()
       
   434 */
       
   435 QSqlQuery QSqlQueryModel::query() const
       
   436 {
       
   437     Q_D(const QSqlQueryModel);
       
   438     return d->query;
       
   439 }
       
   440 
       
   441 /*!
       
   442     Returns information about the last error that occurred on the
       
   443     database.
       
   444 
       
   445     \sa query()
       
   446 */
       
   447 QSqlError QSqlQueryModel::lastError() const
       
   448 {
       
   449     Q_D(const QSqlQueryModel);
       
   450     return d->error;
       
   451 }
       
   452 
       
   453 /*!
       
   454    Protected function which allows derived classes to set the value of
       
   455    the last error that occurred on the database to \a error.
       
   456 
       
   457    \sa lastError()
       
   458 */
       
   459 void QSqlQueryModel::setLastError(const QSqlError &error)
       
   460 {
       
   461     Q_D(QSqlQueryModel);
       
   462     d->error = error;
       
   463 }
       
   464 
       
   465 /*!
       
   466     Returns the record containing information about the fields of the
       
   467     current query. If \a row is the index of a valid row, the record
       
   468     will be populated with values from that row.
       
   469 
       
   470     If the model is not initialized, an empty record will be
       
   471     returned.
       
   472 
       
   473     \sa QSqlRecord::isEmpty()
       
   474 */
       
   475 QSqlRecord QSqlQueryModel::record(int row) const
       
   476 {
       
   477     Q_D(const QSqlQueryModel);
       
   478     if (row < 0)
       
   479         return d->rec;
       
   480 
       
   481     QSqlRecord rec = d->rec;
       
   482     for (int i = 0; i < rec.count(); ++i)
       
   483         rec.setValue(i, data(createIndex(row, i), Qt::EditRole));
       
   484     return rec;
       
   485 }
       
   486 
       
   487 /*! \overload
       
   488 
       
   489     Returns an empty record containing information about the fields
       
   490     of the current query.
       
   491 
       
   492     If the model is not initialized, an empty record will be
       
   493     returned.
       
   494 
       
   495     \sa QSqlRecord::isEmpty()
       
   496  */
       
   497 QSqlRecord QSqlQueryModel::record() const
       
   498 {
       
   499     Q_D(const QSqlQueryModel);
       
   500     return d->rec;
       
   501 }
       
   502 
       
   503 /*!
       
   504     Inserts \a count columns into the model at position \a column. The
       
   505     \a parent parameter must always be an invalid QModelIndex, since
       
   506     the model does not support parent-child relationships.
       
   507 
       
   508     Returns true if \a column is within bounds; otherwise returns false.
       
   509 
       
   510     By default, inserted columns are empty. To fill them with data,
       
   511     reimplement data() and handle any inserted column separately:
       
   512 
       
   513     \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 23
       
   514 
       
   515     \sa removeColumns()
       
   516 */
       
   517 bool QSqlQueryModel::insertColumns(int column, int count, const QModelIndex &parent)
       
   518 {
       
   519     Q_D(QSqlQueryModel);
       
   520     if (count <= 0 || parent.isValid() || column < 0 || column > d->rec.count())
       
   521         return false;
       
   522 
       
   523     beginInsertColumns(parent, column, column + count - 1);
       
   524     for (int c = 0; c < count; ++c) {
       
   525         QSqlField field;
       
   526         field.setReadOnly(true);
       
   527         field.setGenerated(false);
       
   528         d->rec.insert(column, field);
       
   529         if (d->colOffsets.size() < d->rec.count()) {
       
   530             int nVal = d->colOffsets.isEmpty() ? 0 : d->colOffsets[d->colOffsets.size() - 1];
       
   531             d->colOffsets.append(nVal);
       
   532             Q_ASSERT(d->colOffsets.size() >= d->rec.count());
       
   533         }
       
   534         for (int i = column + 1; i < d->colOffsets.count(); ++i)
       
   535             ++d->colOffsets[i];
       
   536     }
       
   537     endInsertColumns();
       
   538     return true;
       
   539 }
       
   540 
       
   541 /*!
       
   542     Removes \a count columns from the model starting from position \a
       
   543     column. The \a parent parameter must always be an invalid
       
   544     QModelIndex, since the model does not support parent-child
       
   545     relationships.
       
   546 
       
   547     Removing columns effectively hides them. It does not affect the
       
   548     underlying QSqlQuery.
       
   549 
       
   550     Returns true if the columns were removed; otherwise returns false.
       
   551  */
       
   552 bool QSqlQueryModel::removeColumns(int column, int count, const QModelIndex &parent)
       
   553 {
       
   554     Q_D(QSqlQueryModel);
       
   555     if (count <= 0 || parent.isValid() || column < 0 || column >= d->rec.count())
       
   556         return false;
       
   557 
       
   558     beginRemoveColumns(parent, column, column + count - 1);
       
   559 
       
   560     int i;
       
   561     for (i = 0; i < count; ++i)
       
   562         d->rec.remove(column);
       
   563     for (i = column; i < d->colOffsets.count(); ++i)
       
   564         d->colOffsets[i] -= count;
       
   565 
       
   566     endRemoveColumns();
       
   567     return true;
       
   568 }
       
   569 
       
   570 /*!
       
   571     Returns the index of the value in the database result set for the
       
   572     given \a item in the model.
       
   573 
       
   574     The return value is identical to \a item if no columns or rows
       
   575     have been inserted, removed, or moved around.
       
   576 
       
   577     Returns an invalid model index if \a item is out of bounds or if
       
   578     \a item does not point to a value in the result set.
       
   579 
       
   580     \sa QSqlTableModel::indexInQuery(), insertColumns(), removeColumns()
       
   581 */
       
   582 QModelIndex QSqlQueryModel::indexInQuery(const QModelIndex &item) const
       
   583 {
       
   584     Q_D(const QSqlQueryModel);
       
   585     if (item.column() < 0 || item.column() >= d->rec.count()
       
   586         || !d->rec.isGenerated(item.column()))
       
   587         return QModelIndex();
       
   588     return createIndex(item.row(), item.column() - d->colOffsets[item.column()],
       
   589                        item.internalPointer());
       
   590 }
       
   591 
       
   592 QT_END_NAMESPACE