1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtSql module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "private/qsqlcachedresult_p.h"
41 
42 #include <qvariant.h>
43 #include <qdatetime.h>
44 #include <qvector.h>
45 #include <QtSql/private/qsqldriver_p.h>
46 
47 QT_BEGIN_NAMESPACE
48 
49 /*
50    QSqlCachedResult is a convenience class for databases that only allow
51    forward only fetching. It will cache all the results so we can iterate
52    backwards over the results again.
53 
54    All you need to do is to inherit from QSqlCachedResult and reimplement
55    gotoNext(). gotoNext() will have a reference to the internal cache and
56    will give you an index where you can start filling in your data. Special
57    case: If the user actually wants a forward-only query, idx will be -1
58    to indicate that we are not interested in the actual values.
59 */
60 
61 static const uint initial_cache_size = 128;
62 
QSqlCachedResultPrivate(QSqlCachedResult * q,const QSqlDriver * drv)63 QSqlCachedResultPrivate::QSqlCachedResultPrivate(QSqlCachedResult *q, const QSqlDriver *drv)
64     : QSqlResultPrivate(q, drv),
65       rowCacheEnd(0),
66       colCount(0),
67       atEnd(false)
68 {
69 }
70 
cleanup()71 void QSqlCachedResultPrivate::cleanup()
72 {
73     cache.clear();
74     atEnd = false;
75     colCount = 0;
76     rowCacheEnd = 0;
77 }
78 
init(int count,bool fo)79 void QSqlCachedResultPrivate::init(int count, bool fo)
80 {
81     Q_ASSERT(count);
82     cleanup();
83     forwardOnly = fo;
84     colCount = count;
85     if (fo) {
86         cache.resize(count);
87         rowCacheEnd = count;
88     } else {
89         cache.resize(initial_cache_size * count);
90     }
91 }
92 
nextIndex()93 int QSqlCachedResultPrivate::nextIndex()
94 {
95     if (forwardOnly)
96         return 0;
97     int newIdx = rowCacheEnd;
98     if (newIdx + colCount > cache.size())
99         cache.resize(qMin(cache.size() * 2, cache.size() + 10000));
100     rowCacheEnd += colCount;
101 
102     return newIdx;
103 }
104 
canSeek(int i) const105 bool QSqlCachedResultPrivate::canSeek(int i) const
106 {
107     if (forwardOnly || i < 0)
108         return false;
109     return rowCacheEnd >= (i + 1) * colCount;
110 }
111 
revertLast()112 void QSqlCachedResultPrivate::revertLast()
113 {
114     if (forwardOnly)
115         return;
116     rowCacheEnd -= colCount;
117 }
118 
cacheCount() const119 inline int QSqlCachedResultPrivate::cacheCount() const
120 {
121     Q_ASSERT(!forwardOnly);
122     Q_ASSERT(colCount);
123     return rowCacheEnd / colCount;
124 }
125 
126 //////////////
127 
QSqlCachedResult(QSqlCachedResultPrivate & d)128 QSqlCachedResult::QSqlCachedResult(QSqlCachedResultPrivate &d)
129     : QSqlResult(d)
130 {
131 }
132 
init(int colCount)133 void QSqlCachedResult::init(int colCount)
134 {
135     Q_D(QSqlCachedResult);
136     d->init(colCount, isForwardOnly());
137 }
138 
fetch(int i)139 bool QSqlCachedResult::fetch(int i)
140 {
141     Q_D(QSqlCachedResult);
142     if ((!isActive()) || (i < 0))
143         return false;
144     if (at() == i)
145         return true;
146     if (d->forwardOnly) {
147         // speed hack - do not copy values if not needed
148         if (at() > i || at() == QSql::AfterLastRow)
149             return false;
150         while(at() < i - 1) {
151             if (!gotoNext(d->cache, -1))
152                 return false;
153             setAt(at() + 1);
154         }
155         if (!gotoNext(d->cache, 0))
156             return false;
157         setAt(at() + 1);
158         return true;
159     }
160     if (d->canSeek(i)) {
161         setAt(i);
162         return true;
163     }
164     if (d->rowCacheEnd > 0)
165         setAt(d->cacheCount());
166     while (at() < i + 1) {
167         if (!cacheNext()) {
168             if (d->canSeek(i))
169                 break;
170             return false;
171         }
172     }
173     setAt(i);
174 
175     return true;
176 }
177 
fetchNext()178 bool QSqlCachedResult::fetchNext()
179 {
180     Q_D(QSqlCachedResult);
181     if (d->canSeek(at() + 1)) {
182         setAt(at() + 1);
183         return true;
184     }
185     return cacheNext();
186 }
187 
fetchPrevious()188 bool QSqlCachedResult::fetchPrevious()
189 {
190     return fetch(at() - 1);
191 }
192 
fetchFirst()193 bool QSqlCachedResult::fetchFirst()
194 {
195     Q_D(QSqlCachedResult);
196     if (d->forwardOnly && at() != QSql::BeforeFirstRow) {
197         return false;
198     }
199     if (d->canSeek(0)) {
200         setAt(0);
201         return true;
202     }
203     return cacheNext();
204 }
205 
fetchLast()206 bool QSqlCachedResult::fetchLast()
207 {
208     Q_D(QSqlCachedResult);
209     if (d->atEnd) {
210         if (d->forwardOnly)
211             return false;
212         else
213             return fetch(d->cacheCount() - 1);
214     }
215 
216     int i = at();
217     while (fetchNext())
218         ++i; /* brute force */
219     if (d->forwardOnly && at() == QSql::AfterLastRow) {
220         setAt(i);
221         return true;
222     } else {
223         return fetch(i);
224     }
225 }
226 
data(int i)227 QVariant QSqlCachedResult::data(int i)
228 {
229     Q_D(const QSqlCachedResult);
230     int idx = d->forwardOnly ? i : at() * d->colCount + i;
231     if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
232         return QVariant();
233 
234     return d->cache.at(idx);
235 }
236 
isNull(int i)237 bool QSqlCachedResult::isNull(int i)
238 {
239     Q_D(const QSqlCachedResult);
240     int idx = d->forwardOnly ? i : at() * d->colCount + i;
241     if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
242         return true;
243 
244     return d->cache.at(idx).isNull();
245 }
246 
cleanup()247 void QSqlCachedResult::cleanup()
248 {
249     Q_D(QSqlCachedResult);
250     setAt(QSql::BeforeFirstRow);
251     setActive(false);
252     d->cleanup();
253 }
254 
clearValues()255 void QSqlCachedResult::clearValues()
256 {
257     Q_D(QSqlCachedResult);
258     setAt(QSql::BeforeFirstRow);
259     d->rowCacheEnd = 0;
260     d->atEnd = false;
261 }
262 
cacheNext()263 bool QSqlCachedResult::cacheNext()
264 {
265     Q_D(QSqlCachedResult);
266     if (d->atEnd)
267         return false;
268 
269     if(isForwardOnly()) {
270         d->cache.resize(d->colCount);
271     }
272 
273     if (!gotoNext(d->cache, d->nextIndex())) {
274         d->revertLast();
275         d->atEnd = true;
276         return false;
277     }
278     setAt(at() + 1);
279     return true;
280 }
281 
colCount() const282 int QSqlCachedResult::colCount() const
283 {
284     Q_D(const QSqlCachedResult);
285     return d->colCount;
286 }
287 
cache()288 QSqlCachedResult::ValueCache &QSqlCachedResult::cache()
289 {
290     Q_D(QSqlCachedResult);
291     return d->cache;
292 }
293 
virtual_hook(int id,void * data)294 void QSqlCachedResult::virtual_hook(int id, void *data)
295 {
296     QSqlResult::virtual_hook(id, data);
297 }
298 
detachFromResultSet()299 void QSqlCachedResult::detachFromResultSet()
300 {
301     cleanup();
302 }
303 
setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy)304 void QSqlCachedResult::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy)
305 {
306     QSqlResult::setNumericalPrecisionPolicy(policy);
307     cleanup();
308 }
309 
310 
311 QT_END_NAMESPACE
312