src/sql/kernel/qsqlcachedresult.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 "private/qsqlcachedresult_p.h"
       
    43 
       
    44 #include <qvariant.h>
       
    45 #include <qdatetime.h>
       
    46 #include <qvector.h>
       
    47 
       
    48 QT_BEGIN_NAMESPACE
       
    49 
       
    50 /*
       
    51    QSqlCachedResult is a convenience class for databases that only allow
       
    52    forward only fetching. It will cache all the results so we can iterate
       
    53    backwards over the results again.
       
    54 
       
    55    All you need to do is to inherit from QSqlCachedResult and reimplement
       
    56    gotoNext(). gotoNext() will have a reference to the internal cache and
       
    57    will give you an index where you can start filling in your data. Special
       
    58    case: If the user actually wants a forward-only query, idx will be -1
       
    59    to indicate that we are not interested in the actual values.
       
    60 */
       
    61 
       
    62 static const uint initial_cache_size = 128;
       
    63 
       
    64 class QSqlCachedResultPrivate
       
    65 {
       
    66 public:
       
    67     QSqlCachedResultPrivate();
       
    68     bool canSeek(int i) const;
       
    69     inline int cacheCount() const;
       
    70     void init(int count, bool fo);
       
    71     void cleanup();
       
    72     int nextIndex();
       
    73     void revertLast();
       
    74 
       
    75     QSqlCachedResult::ValueCache cache;
       
    76     int rowCacheEnd;
       
    77     int colCount;
       
    78     bool forwardOnly;
       
    79     bool atEnd;
       
    80 };
       
    81 
       
    82 QSqlCachedResultPrivate::QSqlCachedResultPrivate():
       
    83     rowCacheEnd(0), colCount(0), forwardOnly(false), atEnd(false)
       
    84 {
       
    85 }
       
    86 
       
    87 void QSqlCachedResultPrivate::cleanup()
       
    88 {
       
    89     cache.clear();
       
    90     forwardOnly = false;
       
    91     atEnd = false;
       
    92     colCount = 0;
       
    93     rowCacheEnd = 0;
       
    94 }
       
    95 
       
    96 void QSqlCachedResultPrivate::init(int count, bool fo)
       
    97 {
       
    98     Q_ASSERT(count);
       
    99     cleanup();
       
   100     forwardOnly = fo;
       
   101     colCount = count;
       
   102     if (fo) {
       
   103         cache.resize(count);
       
   104         rowCacheEnd = count;
       
   105     } else {
       
   106         cache.resize(initial_cache_size * count);
       
   107     }
       
   108 }
       
   109 
       
   110 int QSqlCachedResultPrivate::nextIndex()
       
   111 {
       
   112     if (forwardOnly)
       
   113         return 0;
       
   114     int newIdx = rowCacheEnd;
       
   115     if (newIdx + colCount > cache.size())
       
   116         cache.resize(qMin(cache.size() * 2, cache.size() + 10000));
       
   117     rowCacheEnd += colCount;
       
   118 
       
   119     return newIdx;
       
   120 }
       
   121 
       
   122 bool QSqlCachedResultPrivate::canSeek(int i) const
       
   123 {
       
   124     if (forwardOnly || i < 0)
       
   125         return false;
       
   126     return rowCacheEnd >= (i + 1) * colCount;
       
   127 }
       
   128 
       
   129 void QSqlCachedResultPrivate::revertLast()
       
   130 {
       
   131     if (forwardOnly)
       
   132         return;
       
   133     rowCacheEnd -= colCount;
       
   134 }
       
   135 
       
   136 inline int QSqlCachedResultPrivate::cacheCount() const
       
   137 {
       
   138     Q_ASSERT(!forwardOnly);
       
   139     Q_ASSERT(colCount);
       
   140     return rowCacheEnd / colCount;
       
   141 }
       
   142 
       
   143 //////////////
       
   144 
       
   145 QSqlCachedResult::QSqlCachedResult(const QSqlDriver * db): QSqlResult (db)
       
   146 {
       
   147     d = new QSqlCachedResultPrivate();
       
   148 }
       
   149 
       
   150 QSqlCachedResult::~QSqlCachedResult()
       
   151 {
       
   152     delete d;
       
   153 }
       
   154 
       
   155 void QSqlCachedResult::init(int colCount)
       
   156 {
       
   157     d->init(colCount, isForwardOnly());
       
   158 }
       
   159 
       
   160 bool QSqlCachedResult::fetch(int i)
       
   161 {
       
   162     if ((!isActive()) || (i < 0))
       
   163         return false;
       
   164     if (at() == i)
       
   165         return true;
       
   166     if (d->forwardOnly) {
       
   167         // speed hack - do not copy values if not needed
       
   168         if (at() > i || at() == QSql::AfterLastRow)
       
   169             return false;
       
   170         while(at() < i - 1) {
       
   171             if (!gotoNext(d->cache, -1))
       
   172                 return false;
       
   173             setAt(at() + 1);
       
   174         }
       
   175         if (!gotoNext(d->cache, 0))
       
   176             return false;
       
   177         setAt(at() + 1);
       
   178         return true;
       
   179     }
       
   180     if (d->canSeek(i)) {
       
   181         setAt(i);
       
   182         return true;
       
   183     }
       
   184     if (d->rowCacheEnd > 0)
       
   185         setAt(d->cacheCount());
       
   186     while (at() < i + 1) {
       
   187         if (!cacheNext()) {
       
   188             if (d->canSeek(i))
       
   189                 break;
       
   190             return false;
       
   191         }
       
   192     }
       
   193     setAt(i);
       
   194 
       
   195     return true;
       
   196 }
       
   197 
       
   198 bool QSqlCachedResult::fetchNext()
       
   199 {
       
   200     if (d->canSeek(at() + 1)) {
       
   201         setAt(at() + 1);
       
   202         return true;
       
   203     }
       
   204     return cacheNext();
       
   205 }
       
   206 
       
   207 bool QSqlCachedResult::fetchPrevious()
       
   208 {
       
   209     return fetch(at() - 1);
       
   210 }
       
   211 
       
   212 bool QSqlCachedResult::fetchFirst()
       
   213 {
       
   214     if (d->forwardOnly && at() != QSql::BeforeFirstRow) {
       
   215         return false;
       
   216     }
       
   217     if (d->canSeek(0)) {
       
   218         setAt(0);
       
   219         return true;
       
   220     }
       
   221     return cacheNext();
       
   222 }
       
   223 
       
   224 bool QSqlCachedResult::fetchLast()
       
   225 {
       
   226     if (d->atEnd) {
       
   227         if (d->forwardOnly)
       
   228             return false;
       
   229         else
       
   230             return fetch(d->cacheCount() - 1);
       
   231     }
       
   232 
       
   233     int i = at();
       
   234     while (fetchNext())
       
   235         ++i; /* brute force */
       
   236     if (d->forwardOnly && at() == QSql::AfterLastRow) {
       
   237         setAt(i);
       
   238         return true;
       
   239     } else {
       
   240         return fetch(i);
       
   241     }
       
   242 }
       
   243 
       
   244 QVariant QSqlCachedResult::data(int i)
       
   245 {
       
   246     int idx = d->forwardOnly ? i : at() * d->colCount + i;
       
   247     if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
       
   248         return QVariant();
       
   249 
       
   250     return d->cache.at(idx);
       
   251 }
       
   252 
       
   253 bool QSqlCachedResult::isNull(int i)
       
   254 {
       
   255     int idx = d->forwardOnly ? i : at() * d->colCount + i;
       
   256     if (i > d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
       
   257         return true;
       
   258 
       
   259     return d->cache.at(idx).isNull();
       
   260 }
       
   261 
       
   262 void QSqlCachedResult::cleanup()
       
   263 {
       
   264     setAt(QSql::BeforeFirstRow);
       
   265     setActive(false);
       
   266     d->cleanup();
       
   267 }
       
   268 
       
   269 void QSqlCachedResult::clearValues()
       
   270 {
       
   271     setAt(QSql::BeforeFirstRow);
       
   272     d->rowCacheEnd = 0;
       
   273     d->atEnd = false;
       
   274 }
       
   275 
       
   276 bool QSqlCachedResult::cacheNext()
       
   277 {
       
   278     if (d->atEnd)
       
   279         return false;
       
   280 
       
   281     if (!gotoNext(d->cache, d->nextIndex())) {
       
   282         d->revertLast();
       
   283         d->atEnd = true;
       
   284         return false;
       
   285     }
       
   286     setAt(at() + 1);
       
   287     return true;
       
   288 }
       
   289 
       
   290 int QSqlCachedResult::colCount() const
       
   291 {
       
   292     return d->colCount;
       
   293 }
       
   294 
       
   295 QSqlCachedResult::ValueCache &QSqlCachedResult::cache()
       
   296 {
       
   297     return d->cache;
       
   298 }
       
   299 
       
   300 void QSqlCachedResult::virtual_hook(int id, void *data)
       
   301 {
       
   302     switch (id) {
       
   303     case QSqlResult::DetachFromResultSet:
       
   304     case QSqlResult::SetNumericalPrecision:
       
   305         cleanup();
       
   306         break;
       
   307     default:
       
   308         QSqlResult::virtual_hook(id, data);
       
   309     }
       
   310 }
       
   311 
       
   312 
       
   313 QT_END_NAMESPACE